Ответ 1
Утилита CurrentMemoryUsage, с которой вы связаны, сообщает о рабочем размере вашего приложения. Рабочий набор - это общее количество страниц адресного пространства виртуальной памяти, которые сопоставляются с адресами физической памяти. Однако некоторые или многие из этих страниц могут иметь очень мало фактических данных, хранящихся в них. Таким образом, рабочий набор является "верхней границей" того, сколько памяти используется вашим процессом. Он указывает, сколько адресного пространства зарезервировано для использования, но оно не указывает, сколько фактически выполнено (фактически находится в физической памяти) или сколько страниц, которые были совершены, фактически используются вашим приложением.
Попробуйте следующее: после того, как вы увидите, что размер рабочего набора увеличился после нескольких тестовых прогонов, скройте главное окно приложения. Скорее всего, вы увидите значительное снижение размера рабочего набора. Зачем? Поскольку Windows выполняет вызов SetProcessWorkingSetSize (-1), когда вы сворачиваете приложение, которое отбрасывает неиспользуемые страницы и сжимает рабочий набор до минимума. ОС не делает этого, в то время как окно приложения имеет нормальный размер, потому что слишком часто уменьшает размер рабочего набора, что может ухудшить производительность за счет принудительной перезагрузки данных из файла подкачки.
Чтобы получить более подробное описание: ваше приложение Delphi выделяет память в довольно небольших фрагментах - здесь строка, класс. Среднее выделение памяти для программы обычно составляет менее нескольких сотен байт. Трудно управлять небольшими выделениями, как это эффективно, в масштабах всей системы, поэтому операционная система этого не делает. Он эффективно управляет большими блоками памяти, особенно при размере страницы виртуальной памяти 4k и минимальном размере диапазона адресов виртуальной памяти 64k.
Это представляет проблему для приложений: приложения обычно выделяют небольшие куски, но ОС выделяет память в довольно больших кусках. Что делать? Ответ: suballocate.
Диспетчер памяти библиотеки времени Delphi и менеджер замещающей памяти FastMM (и библиотеки времени выполнения практически любого другого языка или набора инструментов на планете) существуют, чтобы сделать одно: вырезать большие блоки памяти из ОС на более мелкие блоки используемых приложением. Отслеживание того, где все маленькие блоки, насколько они велики, и были ли они "просочились", также требует некоторой памяти - накладные расходы.
В ситуациях с интенсивным распределением/освобождением памяти могут возникать ситуации, когда вы освобождаете 99% от того, что вы выделили, но размер рабочего набора процесса сокращается, скажем, на 50%. Зачем? Чаще всего это вызвано фрагментацией кучи: один небольшой блок памяти все еще используется в одном из больших блоков, который менеджер памяти Delphi получил из ОС и разворачивается внутри. Внутренний подсчет используемой памяти невелик (скажем, 300 байтов), но поскольку он препятствует тому, чтобы кучный менеджер освобождал большой блок, что он вернулся к ОС, вклад рабочего набора этого небольшого 300-байтового фрагмента больше похож на 4k (или 64k в зависимости от того, виртуальные ли они или виртуальное адресное пространство - я не могу вспомнить).
В интенсивной работе с интенсивной памятью, содержащей мегабайты небольших распределений памяти, фрагментация кучи очень распространена, особенно если выделение памяти для вещей, не связанных с интенсивной операцией памяти, происходит одновременно с большой работой. Например, если хруст через операцию с базой данных 80 МБ также выводит статус в список, по мере его продвижения, строки, используемые для отчета о статусе, будут разбросаны в куче среди блоков памяти базы данных. Когда вы отпускаете все блоки памяти, используемые вычислением базы данных, строки списка все еще там (используются, а не потеряны), но они разбросаны повсюду, потенциально занимая весь большой блок ОС для каждой маленькой строки.
Попробуйте использовать трюк окна минимизации, чтобы убедиться, что это уменьшает ваш рабочий набор. Если это так, вы можете оспорить очевидную "серьезность" чисел, возвращаемых счетчиком рабочего набора. Вы также можете добавить вызов SetProcessWorkingSetSize после большой вычислительной операции, чтобы очистить страницы, которые больше не используются.