Еще одна проблема с утечкой памяти (память все еще отсутствует, когда программа завершается) - программа C на SLES

Я запускаю свою программу C на Suse Linux Enterprise, которая сжимает несколько тысяч больших файлов (размером от 10 до 100 МБ), и программа становится все медленнее и медленнее по мере выполнения программы (она работает в многопоточном режиме с 32 потоками на плате Intel Sandy Bridge. ). Когда программа завершается и запускается снова, она все еще очень медленная.

Когда я смотрю, как работает программа, я вижу, что во время работы программы память истощается, что, как вы могли бы подумать, является просто классической проблемой утечки памяти. Но при нормальном несоответствии malloc () / free () я бы ожидал, что вся память вернется, когда программа завершится. Но большая часть памяти не восстанавливается после завершения программы. Команда free или top показывает Mem: 63996M total, 63724M used, 272M free, когда программа замедляется до остановки, но после завершения объем свободной памяти увеличивается только до 3660M. При повторном запуске программы свободная память быстро исчерпывается.

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

Я подумал, что это может быть проблема фрагментации памяти, но я создал небольшую тестовую программу, которая имитирует всю деятельность по распределению памяти в программе (многие рандомизированные аспекты были встроены - размер / количество), и она всегда возвращает всю память при завершение. Так что я не думаю, что это все.

Вопросы:

  1. Может ли быть несоответствие malloc () / free (), которое приведет к потере памяти навсегда, то есть даже после завершения процесса?

  2. Какие еще вещи в программе C (не C ++) могут вызвать необратимую потерю памяти, то есть после завершения программы и даже закрытия окна терминала? Только перезагрузка возвращает память. Я читал другие сообщения о том, что файлы, которые не закрываются, вызывают проблемы, но я не думаю, что у меня есть эта проблема.

  3. Допустимо ли смотреть на верхнюю и свободную статистику памяти, т.е. точно ли они описывают ситуацию с памятью? Кажется, они соответствуют медлительности программы.

  4. Если программа показывает использование памяти только на 4%, найдет ли эта проблема что-нибудь вроде valgrind?


person Keith Hogan    schedule 08.09.2012    source источник
comment
Что произойдет, если вы подождете час или около того? Память возвращается? Ваша программа порождает другие процессы? Сколько барана у вас в системе? Вы используете mmap? Что произойдет, если вы удалите записи из своего кода?   -  person Alex Brown    schedule 08.09.2012
comment
Код слишком велик для публикации. Никаких других процессов не было, только потоки. 16 ГБ оперативной памяти. Без использования mmap. Вы про удаление файла пишет?   -  person Keith Hogan    schedule 08.09.2012
comment
Пустые домыслы, но когда программа вышла, прежде чем снова запустить ее, активен ли жесткий диск? Если да, то останется ли он активным навсегда или в конечном итоге остановится, и если в конечном итоге остановится, ваша программа все еще будет медленной после ее остановки? Интересно, производят ли 32 потока данных больше, чем может записать ваш диск, и поэтому ваша программа быстро заполняет ОЗУ 64 ГБ данных, которые были записаны из процесса, но еще не синхронизированы с диском, а затем замедляется до истинного ввода-вывода скорость. Затем вся система пыхтит, пока эта обратная запись не будет очищена.   -  person Steve Jessop    schedule 08.09.2012
comment
Конечно, эта теория неверна, если Linux выполняет правильную синхронизацию с диском при выходе из процесса.   -  person Steve Jessop    schedule 08.09.2012
comment
Linux не обязательно синхронизируется с диском при выходе из процесса.   -  person Alex Brown    schedule 08.09.2012
comment
@Alex: спасибо, это то, что я подозревал, но не был уверен. Вот почему вы не хотите споткнуться о шнур питания.   -  person Steve Jessop    schedule 08.09.2012


Ответы (5)


Может ли быть несовпадение malloc () / free (), которое приведет к потере памяти навсегда, т. е. даже после завершения процесса?

Нет, malloc и free, и даже mmap безвредны в этом отношении, и когда процесс завершает работу ОС (SUSE Linux в этом случае) запрашивает обратно всю свою память (если она не используется совместно с каким-либо другим процессом, который все еще работает).

Что еще в программе C (не C ++) может вызвать необратимую потерю памяти, то есть после завершения программы и даже закрытия окна терминала? Только перезагрузка возвращает память. Я читал другие сообщения о том, что файлы, которые не закрываются, вызывают проблемы, но не думаю, что у меня эта проблема.

Подобно malloc / free и mmap, файлы, открытые процессом, автоматически закрываются ОС.

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


Однако, если вы определяете потерю памяти как память, которая не помечается сразу как «свободна», может произойти несколько вещей.

  1. Записи на диск или mmap могут быть на некоторое время кэшированы в RAM. ОС должна сохранять страницы, пока не синхронизирует их с диском.
  2. Файлы, ПРОЧИТАННЫЕ процессом, могут оставаться в памяти, если ОС не имеет ничего другого, чтобы использовать эту оперативную память прямо сейчас - при разумном предположении, что они могут потребоваться в ближайшее время и быстрее прочитать копию, которая уже находится в ОЗУ. Опять же, если ОС или другому процессу требуется некоторая часть этой оперативной памяти, ее можно немедленно удалить.

Обратите внимание: как человек, заплативший за всю мою оперативную память, я бы предпочел, чтобы ОС использовала ВСЕ ее ВСЕГДА, если это помогает хотя бы малейшим образом. Свободная RAM - это потраченная впустую RAM.


Основная проблема с небольшим объемом свободной оперативной памяти - это перегрузка, что означает, что сейчас больше процессов (и ОС) запрашивают или используют оперативную память, чем доступно в системе. Похоже, вы используете около 4 Гб ОЗУ в своих процессах, что может быть проблемой - (и помните, что ОС тоже нужен хороший кусок. Но похоже, что у вас много ОЗУ! Попробуйте запустить половину количества процессов и посмотрите если станет лучше.

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

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

Допустимо ли смотреть на верхнюю и свободную статистику памяти, т.е. точно ли они описывают ситуацию с памятью? Похоже, они соответствуют медленной работе программы.


Да, top и ps - вполне разумные способы посмотреть на память, в частности, на поле RES. Пока не обращайте внимания на поле VIRT. Кроме того:

Чтобы увидеть, что вся система делает с памятью, запустите:

vmstat 10

Пока ваша программа работает и некоторое время после нее. Посмотрите, что происходит с ---memory--- столбцами.

Кроме того, после завершения вашего процесса запустите

cat /proc/meminfo

И разместите результаты в своем вопросе.

Если программа показывает использование памяти только на 4%, найдет ли эту проблему что-нибудь вроде valgrind?

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

person Alex Brown    schedule 08.09.2012
comment
Нет разветвления или выполнения. просто темы. Я все еще вижу проблему, когда сокращаю счетчик потоков до примерно 4, а у меня 16 ГБ ОЗУ, так что этого должно быть достаточно для того, что я пытаюсь сделать с 32 потоками. - person Keith Hogan; 08.09.2012
comment
Хорошо, позвольте мне взглянуть на мой Linux-бокс, мы сможем выяснить, куда ушла ваша оперативная память после завершения работы. - person Alex Brown; 08.09.2012
comment
Я запускал его с различным количеством потоков от 1 до 32. Иногда, независимо от количества потоков, он останавливается. Может быть, это что-то вроде слишком большого количества дескрипторов открытых файлов или какого-то другого такого системного ресурса. - person Keith Hogan; 08.09.2012
comment
могло быть - но вы сказали, что программа завершается? Попробуйте использовать заклинания vmstat и meminfo в моем ответе. - person Alex Brown; 08.09.2012

malloc () / free () работают в куче. Эта память гарантированно будет освобождена ОС после завершения процесса. Возможна утечка памяти даже после завершения процесса выделения с использованием определенных примитивов общей памяти (например, System V IPC). Однако я не думаю, что все это имеет прямое отношение.

Немного отступив, вот вывод слегка загруженного сервера Linux:

$ uptime
 03:30:56 up 72 days,  8:42,  2 users,  load average: 0.06, 0.17, 0.27
$ free -m
             total       used       free     shared    buffers     cached
Mem:         24104      23452        652          0      15821        978
-/+ buffers/cache:       6651      17453
Swap:         3811          5       3806

О нет, всего 652 Мб бесплатно! Правильно? Неправильный.

Всякий раз, когда Linux обращается к блочному устройству (например, к жесткому диску), он ищет любую неиспользуемую память и сохраняет там копию данных. В конце концов, а почему бы и нет? Данные уже находятся в ОЗУ, какой-то программе явно нужны эти данные, а неиспользуемая ОЗУ никому не принесет никакой пользы. Если появляется программа и запрашивает больше памяти, кэшированные данные отбрасываются, чтобы освободить место - до тех пор они могут с таким же успехом повиснуть на них.

Ключом к этому free выводу является не первая строка, а вторая. Да, используется 23,4 ГБ ОЗУ, но для программ, которым это необходимо, доступно 17,4 ГБ. См. Помощь! Linux съел мою оперативную память!, чтобы узнать больше.

Я не могу сказать, почему программа становится медленнее, но постоянное снижение показателя «свободная память» до нуля - это совершенно нормально, а не причина.

person willglynn    schedule 08.09.2012
comment
Тогда, может быть, я смотрю на другую проблему, например, на использование дескрипторов файлов или что-то в этом роде? - person Keith Hogan; 08.09.2012
comment
Дескрипторы файлов также возвращаются ОС при завершении процесса, опять же, за исключением странных межпроцессных вещей. Особенности используемого инструмента могут помочь сфокусировать спекуляцию. - person willglynn; 08.09.2012
comment
Я не согласен с тем, что вторая строка более актуальна. Первая строка - полезная. Рассуждения о второй строке, как вы предлагаете, вводят в заблуждение. 17,4 ГБ недоступны для программ, которые этого хотят, потому что, если бы программы использовали такой объем памяти, для дискового кеша ничего не осталось бы, и производительность упала бы. Это первая линия, на которой стоит сосредоточиться, 652 МБ бесплатно, и это прекрасно. 15 ГБ - это буферы, и вам нужно знать, является ли это достаточным дисковым кешем для вашей нагрузки, чтобы знать, подходит ли этот узел для памяти или нет. - person David Schwartz; 08.09.2012

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

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

Если вы наберете эту команду sysctl vm.min_free_kbytes, система сообщит вам, сколько КБ необходимо освободить. Вероятно, меньше 100 МБ. Так что иметь любую сумму, превышающую эту, совершенно нормально.

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

Например, рассмотрим данные, которые вы записали на диск. Операционная система могла освободить память, в которой хранились эти данные. Но это двойная потеря. Если данные, которые вы записали на диск, позже будут прочитаны, ему придется читать их с диска, а не просто извлекать их из памяти. И если эта память позже понадобится для какой-то другой цели, ей просто нужно будет отменить всю работу, которую она проделала, чтобы освободить ее. Фу. Так что, если системе совершенно не нужна свободная память, она не освободит ее.

person David Schwartz    schedule 08.09.2012
comment
Но когда я запускаю свою программу тестирования фрагментации, и она завершается, или я останавливаю ее на полпути (^ C) с выделенной и не освобожденной памятью, верхние / свободные числа всегда возвращаются туда, где они были, когда я запускал программу. Какая разница с реальной программой? - person Keith Hogan; 08.09.2012
comment
@ user1426181: программа тестирования фрагментации не попадает на диск, верно? ОС продолжает использовать память, в которой когда-либо был дисковый кеш, до тех пор, пока она не понадобится, на всякий случай. Но механизм, с помощью которого он освобождает malloced память при выходе из процесса, заключается в отмене отображения виртуального адресного пространства процесса (что он должен был бы сделать в любом случае, так что в основном это бесплатно). Эта память помечается как свободная, по крайней мере, в моем ограниченном понимании. - person Steve Jessop; 08.09.2012
comment
Правильно, программа фрагментации не попадает на диск. - person Keith Hogan; 08.09.2012
comment
@ user1426181: В таком случае у ОС нет выбора. Если к памяти нельзя обратиться каким-либо образом, она должна освободить ее. Однако, если память содержит копии данных, которые также находятся на диске, тогда к памяти может быть обращена попытка прочитать эти данные с диска. (Есть и другие способы обращения к памяти, но, скорее всего, здесь важен именно этот.) - person David Schwartz; 08.09.2012

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

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

Если это не сработает, попробуйте также удалить все входные файлы для первого запуска.

person J Nance    schedule 08.09.2012

Ответы

  1. Да, в C или C ++ нет требования освобождать память, которая не освобождена обратно в ОС
  2. Есть ли у вас файлы с отображением памяти, дескрипторы открытых файлов для удаленных файлов и т. Д. Linux не удалит файл, пока все ссылки на него не будут удалены. Также linux будет кэшировать файл в памяти на случай, если его нужно будет прочитать снова - использование памяти файлового кеша можно игнорировать, так как ОС будет с этим справляться.
  3. No
  4. Может быть, valgrind выделит случаи, когда памяти нет
person Adrian Cornish    schedule 08.09.2012
comment
В C или C ++ нет требования восстанавливать потерянную malloc память при выходе из процесса, но, вероятно, такое требование есть в Posix, не так ли? Вопрос помечен и упоминается SUSE так же, как и C. - person Steve Jessop; 08.09.2012
comment
Для № 3, если top / free недействительны, как лучше всего определить, какая память доступна? - person Keith Hogan; 08.09.2012
comment
@SteveJessop Итак, если вы запустите это на AdrianOS 1.0, это не имеет значения, полагаться на внешние факторы - это плохо. - person Adrian Cornish; 08.09.2012
comment
@ user1426181 Посмотрите на вывод cat /proc/slabinfo, но есть все виды предостережений - то есть общая библиотека может сообщаться каждым процессом как используемая память, но она загружается только один раз - person Adrian Cornish; 08.09.2012
comment
Он не полагается на внешние факторы, он просит объяснить поведение кода, который, по его мнению, вызывает утечку памяти. Такое поведение наблюдалось в SUSE, а не в AdrianOS, поэтому я не совсем понимаю актуальность информации о том, как это может вести себя в AdrianOS. Все остальные ваши пункты кажутся относящимися к Posix / Linux / SUSE. - person Steve Jessop; 08.09.2012
comment
@SteveJessop Просто потому, что C ведет себя так же, как в ЛЮБОЙ операционной системе в мире - это предел стандарта - то, что делает ОС, не имеет отношения к этому языку - есть основная проблема - SuSe с большей вероятностью будет в порядке, чем код ОП - person Adrian Cornish; 08.09.2012
comment
Итак, если он наблюдал такое же поведение на AdrianOS, тогда он мог подозревать, что необратимая потеря памяти была вызвана утечкой malloc / free. Поскольку он наблюдал это в SUSE, он может сделать вывод, что это связано не только с утечкой malloc / free в его коде, но должно быть что-то еще. Поскольку в SUSE (в отличие от AdrianOS) одна только утечка malloc / free не может объяснить наблюдаемое поведение. Правильный? Нормальность SUSE дает нам информацию, которой нет в стандарте C. - person Steve Jessop; 08.09.2012
comment
@SteveJessop Мы можем пойти в чат, если хотите, но упрощенный пример: мой код выделил Гб оперативной памяти, а затем сразу освободил его, но продолжал работать - где требуется вернуть эту память ОС - person Adrian Cornish; 08.09.2012
comment
@SteveJessop Я плохо объясняю свою точку зрения - то, что кажется утечкой, может и не быть - но что бы то ни было - person Adrian Cornish; 08.09.2012
comment
Можно ожидать, что это съест всю свободную память на любом компьютере #include <stdlib.h> int main() { for(;;) { void *ptr=malloc(10000000); free(ptr); } } - person Adrian Cornish; 08.09.2012
comment
У меня этого нет (Windows). 50% ЦП (это одно ядро ​​из двух), частный рабочий набор 500 КБ, размер фиксации 13 МБ. Он просто использует одни и те же 10 МБ снова и снова. Вентилятор стал более шумным, кроме этого на меня это совсем не влияет. Удалите вызов free, и он останется таким же, за исключением частного рабочего набора 6 МБ и размера фиксации 2 ГБ. Это на машине с 2 ГБ ОЗУ, и я с радостью печатаю :-) - person Steve Jessop; 08.09.2012