Подтвердить что ты не робот

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

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

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

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

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

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

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

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

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

125,443 КБ после полного удаления набора данных.

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

179,059 KB после полного удаления набора данных.

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

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

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

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


Спасибо, Дортор и Мейсон, за ваши ответы. Вы заставили меня задуматься и попробовать что-то, что заставило меня понять, что я что-то упустил. Поэтому требуется подробная отладка.

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

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

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

4b9b3361

Ответ 1

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

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

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

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

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

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

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

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

Ответ 2

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

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

Ответ 3

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

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