Память, по-видимому, не освобождается процедурами распределения более низкого уровня после освобождения

Я отлаживаю сложное приложение C ++, десятки тысяч строк, множество вложенных объектов (я говорю это потому, что это может быть актуально для фрагментации памяти), оно также распараллелено OMP / MPI (хотя здесь работает один узел).

Базовый цикл проходит по частям проблемы, при каждом цикле он проходит по всем соответствующим объектам и что-то делает. Эти объекты внутренне кэшируют промежуточные результаты через изменяемые члены. В конце вызывается процедура deCache, где все эти промежуточные результаты должны быть очищены, и мы переходим к следующему фрагменту. Проблема в том, что на этом шаге память не высвобождается, и программе не хватает памяти после нескольких фрагментов.

Я запустил valgrind через отладчик и выпустил подробный магазин моментальных снимков в конце обработки чанка, непосредственно перед дешифрованием и сразу после него. Это показывает потребление памяти в куче с 23 ГБ до 820 МБ, как и ожидалось:

  --------------------------------------------------------------------------------
n        time(i)         total(B)   useful-heap(B) extra-heap(B)    stacks(B)
--------------------------------------------------------------------------------
0 12,019,170,891,847   23,406,329,728   23,015,422,037   390,907,691            0
98.33% (23,015,422,037B) (heap allocation functions) malloc/new/new[], --alloc-fns, etc.
->44.49% (10,414,094,336B) 0x771D63: FTCinvdCdp::FTCinvdCdp(FTCinvdCdp const&) (new_allocator.h:104)
| ->37.49% (8,774,281,216B) 0x5B6F4E: FTCinvdCdpZ::clone() const (stl_construct.h:75
...

сбросить тоже

    -----------------------------------------------------------------------------
n        time(i)         total(B)   useful-heap(B) extra-heap(B)        stacks(B)
--------------------------------------------------------------------------------
0 12,020,946,295,906      857,944,344      830,426,901    27,517,443            0
96.79% (830,426,901B) (heap allocation functions) malloc/new/new[], --alloc-fns, etc.
->21.15% (181,458,432B) 0x712267: void std::vector<GTHSpecSampFunc, std::allocator<GTHSpecSampFunc> >::_M_emplace_back_aux<GTHSpecSampFunc>(GTHSpecSampFunc&&) (new_allocator.h:104)
...

Эти цифры в точности соответствуют ожиданиям. Проблема заключается в том, что объем памяти, показанный сверху, почти не уменьшается (и действительно через некоторое время она исчерпывает память). Запуск массива с параметром —stacks-as-heap указывает на то, что память фактически не освобождена:

--------------------------------------------------------------------------------
n        time(i)         total(B)   useful-heap(B) extra-heap(B)    stacks(B)
--------------------------------------------------------------------------------
0 12,286,840,539,442   24,112,730,112   24,112,730,112             0            0
100.00% (24,112,730,112B) (page allocation syscalls) mmap/mremap/brk, --alloc-fns, etc.
->99.54% (24,000,663,552B) 0x84392D9: mmap (in /lib64/libc-2.12.so)
| ->54.83% (13,220,446,208B) 0x83CB2DF: new_heap (in /lib64/libc-2.12.so)
| | ->53.44% (12,884,901,888B) 0x83CDB19: _int_malloc (in /lib64/libc-2.12.so)
| | | ->53.44% (12,884,901,888B) 0x83CE6AF: malloc (in /lib64/libc-2.12.so)
| | |   ->53.44% (12,884,901,888B) 0x7C74806: operator new(unsigned long) (new_op.cc:49)
| | |     ->28.94% (6,979,321,856B) 0x771D13: FTCinvdCdp::FTCinvdCdp(FTCinvdCdp const&) (new_allocator.h:104)
...

едва меняется на

    --------------------------------------------------------------------------------
n        time(i)         total(B)   useful-heap(B) extra-heap(B)    stacks(B)
--------------------------------------------------------------------------------
0 12,292,664,324,363   23,777,185,792   23,777,185,792             0            0
100.00% (23,777,185,792B) (page allocation syscalls) mmap/mremap/brk, --alloc-fns, etc.
->99.53% (23,665,119,232B) 0x84392D9: mmap (in /lib64/libc-2.12.so)
| ->54.47% (12,952,010,752B) 0x83CB2DF: new_heap (in /lib64/libc-2.12.so)
| | ->53.06% (12,616,466,432B) 0x83CDB19: _int_malloc (in /lib64/libc-2.12.so)
| | | ->53.06% (12,616,466,432B) 0x83CE6AF: malloc (in /lib64/libc-2.12.so)
| | |   ->53.06% (12,616,466,432B) 0x7C74806: operator new(unsigned long) (new_op.cc:49)
| | |     ->28.22% (6,710,886,400B) 0x771D13: FTCinvdCdp::FTCinvdCdp(FTCinvdCdp const&) (new_allocator.h:104)
| | |     | ->24.84% (5,905,580,032B) 0x5B6EFE: FTCinvdCdpZ::clone() const (stl_construct.h:75)
|
...

Я почти уверен, что мы освободили все векторы правильно (с заменой на пустой вектор) и что нет классических утечек памяти (то есть очень последовательное использование автоматических указателей и т. Д.), И кроме того, я ожидал бы, что они будут отображаться при ванили (то есть не Страницы как куча) беги.

Есть идеи о том, что может происходить? Какие ошибки отображаются только при запуске страниц как кучи? Есть ли шанс, что это может быть проблемой фрагментации памяти? Как можно это исправить?

1

Решение

Это типично для систем с виртуальной памятью. Основная процедура «выделения памяти» («brk») действительно только увеличивает размер вашего адресного пространства. Система виртуальной памяти предоставляет страницы фактической памяти по мере необходимости вашему процессу и отбирает их для других процессов по мере необходимости. Таким образом, нет особых причин для перенастройки конца пространства памяти, так как это всего лишь число.

2

Другие решения

Вероятно, решить проблему проще, добавив пространство подкачки в систему, а не исправляя код.

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

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

2) Ваша программа пропускает большие куски памяти. С дополнительным пространством подкачки ваша программа просто немного замедлится, поскольку ядро ​​выяснит, к каким страницам ваша программа все еще не обращается.

3) Ваши векторы патологически фрагментируют виртуальное адресное пространство, создавая точно такие же условия, как (2), без фактической ошибки утечки памяти.

4) Ваша программа патологически пропускает крошечные куски памяти, смешанные с теми, к которым она все еще обращается, создавая условия, подобные (1).

5) Вы справились с практически невозможным сочетанием фрагментации путем выделения / освобождения крошечных объектов, чтобы создать такие же условия, как (4).

Я мог бы догадаться (3) более вероятно. Но не слишком много, и действие на 3, в частности, будет значительным усилием по сравнению с просто увеличением пространства подкачки.

Некоторые дополнительные основы, которые вы, возможно, должны понять. Предполагается, что только очень большие индивидуальные выделения возвращаются из процесса в ОС после освобождения. Если в вашей памяти используется большое количество небольших или средних выделенных ресурсов, то ни один из них не будет возвращен в ОС, поэтому top никогда не должен видеть никакой выпуск в памяти. Но так как вы освобождаете столько памяти, должен консолидировать в рамках процесса и быть доступным для повторного использования с очень небольшой фрагментацией во время следующего пика использования активной памяти программы. Таким образом, одна теория заключается в том, что все, что происходит, — эффективная консолидация во время долины в использовании памяти с последующим эффективным повторным использованием этой памяти в следующем пике. Вы видите что-то неожиданное в top не из-за неисправности, а потому что ваши ожидания ошибочны. Затем происходит сбой программы из-за нехватки памяти не потому, что ей не удалось повторно использовать память, освобожденную с более раннего пика, а потому, что текущий пик использования памяти слишком велик для доступной памяти.

2

Я предлагаю добавить статический член в ваши классы.

static unsigned long count_of_objects;

Увеличьте это значение в вашем конструкторе. Убей это в своем деструкторе.

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

Это покажет, действительно ли ваши объекты удаляются.

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

Из вашего описания я подозреваю проблему освобождения памяти.

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

0
По вопросам рекламы [email protected]