Почему моя память программы 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 и другие структуры, которые я должен был очистить между циклами. Я все еще не уверен, как моя программа работала правильно с дополнительными данными все еще там из более ранних циклов, но это сделало. Я, вероятно, исследую это далее.

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

12
задан Community 23 May 2017 в 10:30
поделиться

2 ответа

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

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

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

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

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

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

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

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

25
ответ дан 2 December 2019 в 05:54
поделиться

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

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

1
ответ дан 2 December 2019 в 05:54
поделиться
Другие вопросы по тегам:

Похожие вопросы: