2010-01-08 5 views
29

Я работаю в Delphi 5 (с установленным FastMM) в проекте Win32 и недавно пытался резко сократить использование памяти в этом приложении. До сих пор я сократил использование почти вдвое, но заметил что-то при работе над отдельной задачей. Когда я минимизировал приложение, использование памяти сократилось с 45 мегабайт до 1 мегабайта, что я приписывал ему подкачку на диск. Когда я восстановил его и перезапустил работу, память увеличилась до 15 мегабайт. По мере того, как я продолжал работать, использование памяти медленно увеличивалось, и сведение к минимуму и восстановление сбросили его до 15 мегабайт. Поэтому, по моему мнению, когда мой код сообщает системе о выпуске памяти, ее все еще держат в соответствии с Windows, и фактическая сборка мусора не срабатывает до тех пор, пока не будет много позже.Можно ли очистить память?

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

Редактировать: Я нашел статью о about.com, которая дает много этого, а также некоторые ссылки и данные для других областей управления памятью.

+2

+1 для странной проблемы. Я вернусь к этому вопросу, чтобы увидеть ваше решение. – fupsduck

+7

Обрезка рабочего набора - это красная селедка при исследовании утечки памяти, fwiw. Process Explorer может показать вам больше статистики, чем диспетчер задач (вам нужно выбрать все необходимые столбцы): http://technet.microsoft.com/en-us/sysinternals/bb896653.aspx –

+0

Теперь я действительно желаю, чтобы я мог принимайте несколько ответов :( –

ответ

16

Это то, что мы используем в DSiWin32:

procedure DSiTrimWorkingSet; 
var 
    hProcess: THandle; 
begin 
    hProcess := OpenProcess(PROCESS_SET_QUOTA, false, GetCurrentProcessId); 
    try 
    SetProcessWorkingSetSize(hProcess, $FFFFFFFF, $FFFFFFFF); 
    finally CloseHandle(hProcess); end; 
end; { DSiTrimWorkingSet } 
+0

Я попробую включить это. Спасибо за код. –

+0

Хорошо, это отлично подходит для обрезки памяти. Теперь, чтобы выяснить, как заставить его работать с более сложными областями моего кода. –

+2

Незначительное обновление: работа над кодом работала лучше недели, и теперь мы запускаем в среднем 5 мегабайт и 40 мегабайт виртуальной памяти по сравнению с 75 и 75 при запуске. –

0

Я читал об этом раньше, но не имею прямого опыта. Вызов WINAPI SetProcessWorkingSetSize() должен «исправить» проблему. Снова у меня нет прямого опыта в этом.

+0

Из описания на MSDN это может быть интересное упражнение (независимо от того, бесполезно, я не знаю), чтобы попытаться использовать get, а затем набор, чтобы заставить попытку обновления. –

+1

Я просмотрел свой архив. Я думаю, что здесь я увидел информацию: http://delphi.about.com/od/windowsshellapi/ss/setprocessworkingsetsize-delphi-program-memory-optimize.htm – fupsduck

+0

Эта ссылка весьма полезна. Мне нужно посмотреть, могу ли я включить это, не убивая мое приложение. Благодарю. –

2

Диспетчер задач не показывает, что использует ваша программа. Он показывает общее количество, которое диспетчер памяти выделил из Windows. Когда вы освобождаете объект или иным образом освобождаете динамически выделенную память, он немедленно возвращается в диспетчер памяти (FastMM). Независимо от того, передано ли это обратно в Windows, другое дело. Менеджер памяти любит держать вокруг себя лишнюю память, поэтому ей не нужно захватывать больше из ОС каждый раз, когда вам нужно создать новый объект. (Это хорошо, и вы не хотите его менять.)

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

+0

Да, я всю неделю проводил уборку и убедился, что все правильно освободилось. Одна вещь, которую я обнаружил два дня назад (я только на третьем курсе, как профессиональный программист), состоит в том, что некоторые типы указателей (TStringList и т. Д.) Не будут связывать с ними объекты, поэтому я пытался очистить все такое. Это сильно сократило размер моего места, но оно все еще растет и не будет плато. Я подозреваю, что FastMM не вернет Windows должным образом, но я не могу быть уверен в это время. Переход к нему с помощью другого приложения для монитора. –

+0

В отличие от многих классов VCL TStringLists необходимо создавать и уничтожать явно с помощью новых и удалять. – fupsduck

+0

Том: Да. Наконец, TStringList получил право собственности на объекты в Delphi 2010, но он не вернулся в D5. Фупсук: Не совсем. TStringList не является классом VCL, он создается со своим конструктором (.Create) и уничтожается методом Free. Новые и Удалить предназначены только для указателей без объектов. –

72

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

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

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

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

Если вы заинтересованы в использовании памяти приложения под Windows, не самый важный номер, а целый ряд соответствующих номеров:

  • Виртуального размер - это общее количество адресного пространства, зарезервированного приложением. Адресное пространство (то есть, какие указатели указывают) может быть безоговорочно, зарезервировано или зафиксировано. Нефиксированная память может быть выделена в будущем, либо менеджер памяти, или при загрузке библиотеки DLL (динамические библиотеки должны пойти где-то в памяти) и т.д.

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

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

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

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

Эти числа не представляют собой непересекающиеся множества. Это разные способы суммирования состояний разных типов страниц. Например, рабочий набор = частный рабочий набор + совместный рабочий набор.

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

О SetProcessWorkingSetSize:

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

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

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

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

+1

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

+14

Барри, у вас есть настоящая ловкость для объяснения сложных тем. Вы должны опубликовать это в своем блоге, поэтому у нас будет место, где люди будут обращаться в будущем, когда они будут спрашивать о памяти процесса. Небеса знают, что этот вопрос достаточно подходит! –

+0

Я согласен с Мэйсоном в этом :) –

5

Давайте сделаем это прямо: FastMM4 не течет память, ваш код может быть.

Чтобы знать наверняка, выполнить эту инструкцию где-то в вашем приложении (где FastMM4 находится в пункте изез и $define ManualLeakReportingControl устанавливается в FastMM4Options.inc, например):

ReportMemoryLeaksOnShutdown := True; 

FastMM4 будет сообщать в конец, если вы забыли освободить память.

Если вы хотите знать немного больше, вы можете смотреть это видео из CodeRage 2: Fighting Memory Leaks for Dummies

+0

Я определенно не буду спорить с тобой. Я кодер номер 4 в этой партии кода и все еще пытаюсь понять, почему некоторые вещи были выполнены так, как они были. Много места для проблем с памятью в этом коде наверняка. Я использовал возможности отчетности FastMM, а также Pascal Analyzer и QATime, чтобы как можно больше исследовать этот код, поэтому я знаю, что убрал много проблем. –

+0

Теперь, если вы уверены, что FastMM4 не сообщает о утечке памяти, если вы столкнулись с запуском на ** Vista, Windows2008 или Windows 7, в ОС ** возникают проблемы с памятью с критическими секциями, хранящими в кэше структуру отладки и ADO утечка памяти каждый раз, когда вы устанавливаете ConnectionString. –

+0

FastMM сообщает о утечке памяти, но это те, о которых я сейчас ничего не могу сделать, и они достаточно незначительны, и я пока не беспокоюсь о них. Я уже получил объем памяти от 75 мегабайт до 40, а 20k ничего не происходит. –

3

После обучения с отличным ответом Барри Келли, попробуйте проанализировать свой процесс, используя VMMap от Sysinternals, которую можно найти here. Это анализирует использование памяти одного процесса более подробно, чем Process Explorer: «VMMap - идеальный инструмент для разработчиков, которые хотят понимать и оптимизировать использование ресурсов памяти своего приложения». Он также имеет полезный файл справки.

+0

+1 для упоминания VMMap –

-1

Недавно у меня была очень похожая проблема с моей программой. См. Мой вопрос: Why does my Delphi program’s memory continue to grow?

Несмотря на то, что я был уверен, что это что-то еще, он оказался серьезным утечкой памяти, вызванным несколькими ошибками в моем коде, чтобы освободить память.

Прежде чем делать что-либо еще, будьте абсолютно уверены, что вы правильно освобождаете всю память.

Смежные вопросы