LINUX.ORG.RU

C++. Предсказуемость поведения менеджера памяти.


0

1

Проверять результат выполнения new/malloc и т.п. - это конечно полезно, но не так прикольно. Не переключайте канал!

Софтина изредка в несколько потоков вызывает new(), прося что-то в районе 1...2 МБ памяти. Подобные запросы случаются одновременно из 4 потоков. Результат вызова не проверяется. Железных ядер - 4 (на всех нижеописанных платформах).

На тестовой платформе с linux 3.4.9 с 32 Гб ОЗУ работала безупречно.

На тестовой платформе с win-7 с 16 Гб ОЗУ иногда валилась. Валилась с одинаковой вероятностью и на виртуалке с 4 Гб ОЗУ.

Казалось бы - 16 гигов! Параллельно фотошопов не загружено. Если смотреть на стандартный «график использования ресурсов», то уровень занятости памяти валяется в нижних 20%.

Почему может так происходить, что при наличии кучи свободной памяти изредка происходят такие обломы? Ясно, что в таком топике на форуме невозможно объяснить и понять логику такой хитрой штуки, как менеджер памяти какой-либо ОС. Но хотя-бы какими его свойствами это может объясняться? Какие причины гипотетически тут могут быть? Он работает асинхронно от вызовов выделения памяти? Вызов на выделение памяти не блокирующий и может произойти в «неудобное время» (идёт процесс переупорядочения свободных блоков) и будет отклонён? Занятость диспетчера памяти своими служебными задачами рассматривается как отказ, равнозначный отсутствию свободной памяти? Почему вызывающий поток не будет заблокирован, чтобы дождаться окончания служебных процедур, если они кратковременные?

А оно точно на выделении памяти падает-то?

anonymous
()

менеджер памяти какой-либо ОС

new()

/0 и ЛПП.

Чем и в чем собираешь программу под винду? MSVC, минг, цыгвынь?

geekless ★★
()

Вряд ли ты первый, кто додумался получать память таким способом, накосячил где-нибудь.

ilovewindows ★★★★★
()

Перечитал первое предложение, сначала глазам не поверил.

Софтина изредка в несколько потоков вызывает new(), прося что-то в районе 1...2 МБ памяти. Подобные запросы случаются одновременно из 4 потоков. Результат вызова не проверяется.

Но теперь таки поверил. Вы что, реально проверяете *возвращаемое значение* оператора new? Или используете его специальную форму, которая не кидает исключений?

yoghurt ★★★★★
()

На тестовой платформе с linux 3.4.9 с 32 Гб ОЗУ работала безупречно.

На тестовой платформе с win-7 с 16 Гб ОЗУ иногда валилась. Валилась с одинаковой вероятностью и на виртуалке с 4 Гб ОЗУ.

Валилась с одинаковой вероятностью и на виртуалке с 4 Гб ОЗУ.

Так а на каком месте валилась то?

yoghurt ★★★★★
()

у меня была ситуация на хостинге на агаве, когда нельзя было выделить больше чем 143 мб на VPS с 256 мб. причем память была, лимиты выбраны не были. алсо, выделение через new работало с большим объемом O_o

ckotinko ☆☆☆
()

Результат вызова не проверяется.

ну так проверь в дебаге, в чем проблема то?

AF ★★★
()

Почему ты описываешь поведение программы как блондинка? Не «иногда валилась», а будь добр хотя бы написать что это (исключение? segfault?) и показать backtrace. Не можешь - иди борщ варить, нечего тебе за компьютером делать.

И нет, проверять результат выполнения new не нужно - без nothrow он либо кидает исключение, либо всегда возвращает корректный указатель. И нет, ты даже близко не понимаешь как работает менеджер памяти, начиная от его связи (на самом деле, её отсутствия) с ОС и заканчивая домыслами о занятости и асинхронности, так что даже не думай на него грешить - с таким уровнем вывод очевидный и очень простой - у тебя просто кривой код. Начни с запуска его под valgrind, он отловит большинство случаев неправильной работы с памятью.

slovazap ★★★★★
()

Не дописал немного, извините. По тексту поста нетрудно догадаться, что раз я акцентирую внимание на выделении памяти и пишу о проблемах выделения памяти, то падает на попытке выделения памяти.

Да, падает на попытке выделения 1...2 Мб с исключением bad_alloc. Под «значение проверяется» имелся ввиду общий смысл проверки результата выполнения операции выделения памяти - либо возвращаемое значение, либо отсутствие исключений и т.п. В моём случае - кидается исключение из оператора new внутри функции reserve() внутри функции resize() внутри std::vector при попытке сделать resize() на 1...2 Мб.

Вопрос о том, почему гипотетически могут появляться отказы в выделении таких относительно мелких кусков памяти при наличии в системе свободных почти 16 гигов, например.

kiverattes ★☆
() автор топика
Ответ на: комментарий от geekless

64, но вряд-ли это влияет на суть вопроса. Даже при 32 и при 3 свободных гигах меня бы удивляло, почему нельзя выделить 1...2 Мб.

kiverattes ★☆
() автор топика
Ответ на: комментарий от kiverattes

Из-за фрагментации, например. Но в данном случае не похоже.

geekless ★★
()
Последнее исправление: geekless (всего исправлений: 1)
Ответ на: комментарий от kiverattes

Ну сделай собственный аллокатор, который просит память через VirtualAlloc, и гоняй тесты. Хоть понятно будет, ядро память не даёт или крестовая библиотека.

geekless ★★
()
Ответ на: комментарий от vazgen05

Нет, я ведь написал - проблема в выделении памяти (кидается bad_alloc из new()), а не в access violation или SIGSEGV каком-нибудь.

kiverattes ★☆
() автор топика
Ответ на: комментарий от geekless

Это немного не то, что хотелось обсудить в топике. Хотелось обсудить «почему так», а не «как лечить». Как лечить - тыща способов, лежащих на поверхности )

kiverattes ★☆
() автор топика

Если бы ты на сях писал, то знал бы, что даже если malloc вернет тебе !NULL, это еще не значит, что тебе действительно дали столько памяти, сколько нужно. А вот если ты ей сделаешь memset(mem, 0, size), и это пройдет без проблем, то все ОК. Кстати, вполне возможно, что с calloc работа упрощается, поэтому им и пользуюсь, в макросе (который проверяет, не было ли ошибок).

А еще полезно вместо free(mem) использовать макрос, который не только освобождает память, но и делает mem = NULL, чтобы где-нибудь дальше ты не имел возможности сделать повторный free()

Anon
()
Ответ на: комментарий от kiverattes

почему гипотетически могут появляться отказы в выделении таких относительно мелких кусков памяти при наличии в системе свободных почти 16 гигов, например

Это С++, детка ☺

Anon
()
Ответ на: комментарий от Anon

Разве malloc может выделить памяти меньше, чем просили и вернуть на неё указатель? Какой-то бред. Зачем это вообще сделано? Как пользователю этим пользоваться?

kiverattes ★☆
() автор топика
Ответ на: комментарий от kiverattes

Может. Ведь тебе дают виртуальную память, а реальной она становится лишь при обращении. Так что, пока ты по всему массиву выделенной памяти не пройдешься, ты не можешь сказать точно, что она у тебя есть. И это правильно, иначе вся память быстренько бы кончилась — всякое УГ, вроде хромых, сожрало бы ее моментально!

Anon
()
Ответ на: комментарий от kiverattes

Разве malloc может выделить памяти меньше, чем просили и вернуть на неё указатель? Какой-то бред. Зачем это вообще сделано? Как пользователю этим пользоваться?

Ты не знаком с устройством unix.

google overcommit

geekless ★★
()
Ответ на: комментарий от Anon

Гнутый calloc точно так же mmap делает и тоже ни черта не аллоцируется, пока в страницу не тыкнешься.

d_a ★★★★★
()
Ответ на: комментарий от d_a

Я же и сказал:

вполне возможно

Потому как лень мне подробности читать. Я думал, что calloc делает после выделения памяти memset

Anon
()
Ответ на: комментарий от Anon

Память виртуальная, но если ОС тебе её выделила, то она должна убицца, но в крайнем случае начать юзать своп при обращении к этой памяти. А говорить о том, что часть выделенной памяти (особенно виртуальной) вдруг станет не моей и я получу SEGSEGV - это какой-то бред )

kiverattes ★☆
() автор топика

Вызов на выделение памяти не блокирующий

Вообще он блокирующий. Вендовое crt (malloc) дёргает HeapAlloc и в качестве флагов передаёт ему 0, что означает, что сериализация не отключена. Так по крайней мере в микрософтовском crt.

Вопрос, чем ты конпиляешь под венду?

invy ★★★★★
()
Последнее исправление: invy (всего исправлений: 1)
Ответ на: комментарий от kiverattes

Ну так если тебе не нравится, ставь vm.overcommit_memory в 2 (/etc/sysctl.conf)

Но в этом случае тебе и 32ГБ оперативы может оказаться мало!

Anon
()
Ответ на: комментарий от kiverattes

Во всех известных мне юниксах по дефолту оверкоммит. Ядро удовлетворяет все запросы на выделение виртуальной памяти, а когда память физически заканчивается, просто убивает самый жирный процесс. И с точки зрения отказоустойчивости сервера, это абсолютно правильно.

В линуксе можно выключить оверкоммит.

В BSD вроде бы нельзя.

В винде по умолчанию нет оверкоммита, можно ли его специально включить — не знаю, но думаю, что нет.

geekless ★★
()
Последнее исправление: geekless (всего исправлений: 2)
Ответ на: комментарий от Anon

Но в этом случае тебе и 32ГБ оперативы может оказаться мало!

ЛПП, у меня на десктопе 4 ГБ ОЗУ и 8 свопа. Оверкоммит выключен.

geekless ★★
()
Ответ на: комментарий от kiverattes

А говорить о том, что часть выделенной памяти (особенно виртуальной) вдруг станет не моей и я получу SEGSEGV - это какой-то бред )

А если представить себе типичную unix-машину и подумать?

geekless ★★
()
Ответ на: комментарий от geekless

Я на 2ГБ оверкоммит отключал, но это приводило к невозможности запуска всякого говна, вроде хромого.

Anon
()

C++
предсказуемость

/0

anonymous
()
Ответ на: комментарий от geekless

Давайте либо на вопросы будем отвечать, либо не отвечать, нафига друг-друга посылать «подумать», «в гугл»? Так далеко не уедем.

kiverattes ★☆
() автор топика
Ответ на: комментарий от Anon

Хромой жрёт как не в себя. Особенно это печально на фоне того, что он кучу раз форкается, и всю эту память тоже надо виртуально дублировать значит. Поэтому Хромой может физически занимать, например 1 гигабайт, но чтобы он работал и не падал, надо еще держать 8 гигов свопа чисто для того, чтобы система знала, что у ней запас по памяти.

geekless ★★
()
Ответ на: комментарий от kiverattes

Я задал вопрос, ответ на который очевиден.

// Эдди, ответь ТСу, почему оверкоммит предпочтителен для сервера. Мне лень писать, я вкусный суп ем. :D

geekless ★★
()

Все твои домыслы про проблемный аллокатор — ерунда. Ищи ошибку в своем коде. Думаю, у тебя либо утечка памяти и, как следствие, заканчивается виртуальное адресное пространство (приложение-то 32-битное?), либо ты допускаешь memory corruption, в результате чего разваливаются внутренние структуры аллокатора.

Relan ★★★★★
()
Ответ на: комментарий от Relan

Нет никаких домыслов про «проблемный аллокатор». Я не думаю, что он «проблемный». Он есть какой есть. Просто хочется понять, почему такое гипотетически может происходить. Вариант с повреждением структур аллокатора интересный, но я по этому поводу ничего не могу сказать, просто запомню как один из вариантов.

Про утечку я наверное косвенно уже написал - при наличии свободных гигабайт происходит проблема выделения 1...2 Мб.

kiverattes ★☆
() автор топика
Ответ на: комментарий от kiverattes

Ты нормален вообще? Ты спрашиваешь, почему оверкоммит не бред. Я отвечаю: потому что на сервере это предпочтительно. Какого тебе еще надо?

но речь не идёт о том

Поциэнт, вы уж определитесь. А то на какой вопрос ни ответь, у вас всё не о том.

geekless ★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.