2016-12-06 8 views
0

Объект пытается выделить больше памяти, а затем разрешенное виртуальное адресное пространство (2Gb на win32). std::bad_alloc пойман и объект выпущен. Использование памяти процесса падает, и процесс должен продолжаться; однако любое последующее распределение памяти не выполняется с другим std::bad_alloc. Проверка использования памяти с помощью VMMap показала, что память кучи высвобождается, но на самом деле она обозначена как конфиденциальная, не оставляя свободного места. Единственное, что нужно сделать, это выйти и перезапустить. Я бы понял проблему фрагментации, но почему процесс не может вернуть память после релиза?«Частная память» не выпущена после обнаружения bad_alloc, несмотря на уничтожение объекта

Объект QListQList s. Приложение многопоточно. Я мог бы сделать небольшой репродуктор, но я мог бы воспроизвести проблему только один раз, в то время как в большинстве случаев воспроизведение может снова использовать освобожденную память.

Действительно ли Qt делает что-то подлое? Или, может быть, это win32 задерживает выпуск?

+0

win32 никогда не «задерживает» выпуск - если вы вызываете 'VirtualFree (p, 0, MEM_RELEASE)' - память будет выпущена при возврате функции (в случае, если курсор 'p' правильный) - так 100%' VirtualFree' не был вызван или вызван с плохим аргументом – RbMm

+0

'QList'' QList's ** what **? Это важно здесь. –

+0

"QVariant". Это в основном электронная таблица. Он обычно содержит числа в виде строк или URL-адресов. – Narcolessico

ответ

1

Ответ Мартина Драб положил меня на правильный путь. Исследование о распределении кучи я нашел этот old message, разъясняющий, что происходит:

Проблемы здесь в том, что блоки над 512k являются прямыми вызовами VirtualAlloc, а все остальное меньше, чем это выделяются из из кучи сегментов. Плохая новость заключается в том, что сегменты никогда не выпустили (полностью или частично), поэтому те, что вы берете на себя весь адрес пространство с небольшими блоками вы не можете использовать их для других куч или блоков более 512 K.

проблемы не связан с Qt, но связан с Windows; Я мог бы, наконец, воспроизвести его с помощью простого std::vector массивов символов. Распределитель кучи по умолчанию оставляет сегменты адресного пространства неизменными даже после того, как соответствующее распределение было явно выпущено. Соотношение состоит в том, что процесс может запрашивать снова буферы с аналогичным размером, а менеджер кучи сэкономит время на повторное использование существующих сегментов адресов вместо уплотнения старых для создания новых.

Обратите внимание, что это не имеет никакого отношения к количеству доступной физической или виртуальной памяти. Это только адресное пространство, которое остается сегментированным, хотя эти сегменты бесплатны.Это серьезная проблема для 32-битных архитектур, где адресное пространство составляет всего 2 ГБ (может быть 3).

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

Чтобы воспроизвести проблему, просто создайте огромный вектор кусков размером меньше 512 КБ (они должны быть выделены с помощью new или malloc). После того, как память будет заполнена и затем отпущена (независимо от того, достигнут ли предел, а исключение поймано или память просто заполнена без ошибок), процесс не сможет выделить ничего большего, чем 512Kb. Память свободна, она назначается одному процессу («частный»), но все ведра слишком малы.

Но есть и худшие новости: по-видимому, нет никакого способа заставить уплотнение сегментов кучи. Я пробовал с this и this, но не повезло; точного эквивалента POSIX fork() (см. here и here). Единственное решение - сделать что-то более низкое, например creating a private heap, и уничтожить его после небольших распределений (как это предложено в приведенном выше сообщении) или реализовать пользовательский распределитель (там может быть какое-то коммерческое решение). Оба совершенно неосуществимы для большого, существующего программного обеспечения, где самым простым решением является закрытие процесса и его перезапуск.

1

Как я понимаю вашу проблему, вы выделяете большие объемы памяти из кучи, которая в какой-то момент не срабатывает. Выпуск памяти обратно в кучу процесса необязательно означает, что диспетчер кучи фактически освобождает виртуальные страницы, содержащие только свободные блоки кучи (из-за причин производительности). Итак, если вы попытаетесь напрямую распределить виртуальную память (VirtualAlloc или VirtualAllocEx), попытка провалится, так как почти вся память потребляется менеджером кучи, который не имеет возможности узнать о вашей попытке прямого распределения.

Ну, что вы можете сделать с этим. Вы можете создать свою собственную кучу (HeapCreate) и ограничить ее максимальный размер. Это может быть довольно сложно, поскольку вам нужно убедить Qt использовать эту кучу.

При распределении больших объемов памяти я рекомендую использовать VirtualAlloc вместо функций кучи. Если запрашиваемый размер составляет> = 512 Кбайт, то для удовлетворения вашего запроса куст-манипулятор использует VirtualAlloc. Тем не менее, я не знаю, действительно ли он выпускает страницы, когда вы освобождаете регион, или он начинает использовать его для удовлетворения других запросов на распределение кучи.

+0

«Объект QList QLists». Никто не выполняет никаких ручных вызовов WINAPI. –

+0

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

 Смежные вопросы

  • Нет связанных вопросов^_^