Почему память моей программы Delphi продолжает расти?

Я использую Delphi 2009, в который встроен менеджер памяти FastMM4.

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

используя процедуру CurrentMemoryUsage, приведенную в ответе spenwarr на:как получить память, используемую программой Delphi, у меня появится памяти FastMM4 в обработка.

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

1,456 КБ используется после запуска моей программы без набора данных.

218,455 КБ используется после загрузки большого набора данных.

71,994 КБ после полной очистки набора данных. Если я выйду в этот момент (или в любой момент в моем примере), никаких утечек памяти не сообщается.

271,905 КБ используется после загрузки того же набора данных снова.

125,443 КБ после полной очистки данных.

325,519 КБ используется после загрузки того же набора данных снова.

179,059 КБ после полной очистки набора данных.

378,752 КБ используется после загрузки того же набора данных снова.

кажется, что использование памяти моей программы растет примерно на 53,400 КБ при каждом цикле загрузки / очистки. Диспетчер задач подтверждает, что это действительно происходит.

Я слышал, что FastMM4 делает не всегда освобождайте всю память программы обратно в операционную систему, когда объекты освобождены, чтобы она могла сохранить некоторую память, когда ей нужно больше. Но этот постоянный рост беспокоит меня. Поскольку об утечках памяти не сообщается, я не могу определить проблему.

кто-нибудь знает, почему это происходит, если плохо, и если я могу что-либо делать?


спасибо dthorpe и Mason за ваши ответы. Ты заставляешь меня думать и пытаться. это заставило меня понять, что я что-то упускаю. Поэтому требовалась детальная отладка.

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

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

этот вопрос был дан ответ. Спасибо за помощь.

3 ответов


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

попробуйте следующее: после того, как вы увидите, что ваш рабочий размер набора ползет вверх после нескольких тестовых запусков, минимизируйте главное окно вашего приложения. Скорее всего, вы увидите, что размер рабочего набора значительно уменьшится. Почему? Поскольку Windows выполняет вызов SetProcessWorkingSetSize (-1) при сворачивании приложения, которое сбрасывает неиспользуемые страницы и сжимает рабочий набор до минимума. ОС не делает этого, пока окно приложения имеет нормальный размер, потому что уменьшение размера рабочего набора слишком часто может ухудшить производительность, заставляя данные перезагружаться из файла подкачки.

чтобы попасть в него более подробно: ваше приложение Delphi выделяет память в довольно небольших кусках - строка здесь, класс там. Среднее выделение памяти для программы обычно составляет менее нескольких сотен байт. Трудно управлять такими небольшими ассигнованиями эффективно в общесистемном масштабе, поэтому операционная система этого не делает. Он эффективно управляет большими блоками памяти, особенно при минимальных размерах страницы виртуальной памяти 4k и диапазона адресов виртуальной памяти 64k.

Это представляет проблему для приложений: приложения обычно выделяют небольшие куски, но ОС распределяет память довольно большими кусками. Что делать? Ответ: перераспределить.

диспетчер памяти библиотеки времени выполнения Delphi и замена FastMM менеджер памяти (и библиотеки времени выполнения почти всех других языков или наборов инструментов на планете) существуют для одного: вырезать большие блоки памяти из ОС в меньшие блоки, используемые приложением. Отслеживание того, где находятся все маленькие блоки, насколько они велики и были ли они "просочились", требует некоторой памяти, также называемой накладными расходами.

в ситуациях тяжелого распределения/освобождения памяти могут быть ситуации, в которых вы освобождаете 99% того, что вы выделили, но размер рабочего набора процесса сокращается только, скажем, на 50%. Почему? Чаще всего это вызвано фрагментацией кучи: один небольшой блок памяти все еще используется в одном из больших блоков, которые диспетчер памяти Delphi получил из ОС и разделил внутри. Внутреннее количество используемой памяти невелико (скажем, 300 байт), но поскольку это мешает менеджеру кучи выпускать большой блок, который он находится обратно в ОС, вклад рабочего набора этого маленького 300 байта chunk больше похож на 4k (или 64k в зависимости от того, виртуальные ли это страницы или виртуальное адресное пространство - я не могу вспомнить).

при интенсивной работе с большой памятью, включающей мегабайты небольших выделений памяти, фрагментация кучи очень распространена , особенно если выделения памяти для вещей, не связанных с интенсивной работой памяти, происходят одновременно с большой работой. Например, если хруст через вашу операцию базы данных 80MB также выводит состояние в listbox как это прогрессирует, строки, используемые для отчета о состоянии, будут разбросаны в куче среди блоков памяти базы данных. Когда вы освобождаете все блоки памяти, используемые вычислением базы данных, строки списка все еще находятся там (в использовании, а не потеряны), но они разбросаны повсюду, потенциально занимая весь большой блок ОС для каждой маленькой строки.

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


какой набор данных вы используете? Если он полностью реализован в Delphi (не вызывая другой код с другим менеджером памяти, например Midas), вы можете попробовать преднамеренно утечку набора данных.

Я предполагаю, что ваш набор данных находится в форме, и он освобождается автоматически, когда форма очищает его компоненты. Попробуйте положить MyDataset := nil; в OnDestroy формы. Это гарантирует утечку набора данных, а также все, что принадлежит набору данных. попробовать что после загрузки один раз и снова после загрузки дважды и сравнить отчеты об утечке, и посмотреть, если это дает вам что-нибудь полезное.


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

см. это для деталей:моя программа никогда не освобождает память. Почему?