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

рассмотреть следующий пример приложения

program TestMemory;


{$APPTYPE CONSOLE}

uses
  PsAPI,
  Windows,
  SysUtils;

function GetUsedMemoryFastMem: cardinal;
var
    st: TMemoryManagerState;
    sb: TSmallBlockTypeState;
begin
    GetMemoryManagerState(st);
    result := st.TotalAllocatedMediumBlockSize + st.TotalAllocatedLargeBlockSize;
    for sb in st.SmallBlockTypeStates do
    begin
        result := result + sb.UseableBlockSize * sb.AllocatedBlockCount;
    end;
end;

function GetUsedMemoryWindows: longint;
var
  ProcessMemoryCounters: TProcessMemoryCounters;
begin
  Result:=0;
  ProcessMemoryCounters.cb := SizeOf(TProcessMemoryCounters);
  if GetProcessMemoryInfo(GetCurrentProcess(), @ProcessMemoryCounters, ProcessMemoryCounters.cb) then
   Result:= ProcessMemoryCounters.WorkingSetSize
  else
   RaiseLastOSError;
end;

procedure Test;
const
  Size = 1024*1024;
var
  P : Pointer;
begin
  GetMem(P,Size);

      Writeln('Inside');
      Writeln('FastMem '+FormatFloat('#,', GetUsedMemoryFastMem));
      Writeln('Windows '+FormatFloat('#,', GetUsedMemoryWindows));
      Writeln('');

  FreeMem(P);
end;

begin
      Writeln('Before');
      Writeln('FastMem '+FormatFloat('#,', GetUsedMemoryFastMem));
      Writeln('Windows '+FormatFloat('#,', GetUsedMemoryWindows));
      Writeln('');

      Test;

      Writeln('After');
      Writeln('FastMem '+FormatFloat('#,', GetUsedMemoryFastMem));
      Writeln('Windows '+FormatFloat('#,', GetUsedMemoryWindows));
      Writeln('');
      Readln;
end.

результаты, возвращаемые приложением,

Before
FastMem 1.844
Windows 3.633.152

Inside
FastMem 1.050.612
Windows 3.637.248

After
FastMem 2.036
Windows 3.633.152

Я хочу знать, почему результаты использования памяти различаются в Before и After:


person Salvador    schedule 15.12.2010    source источник
comment
@Optimal Cynic - это действительно умно? Кажется, что мое приложение не возвращает около 160 МБ. На компьютерах с 1 ГБ или ОЗУ (или меньше) это не пустая трата ОЗУ? Подробности: stackoverflow. ком/вопросы/4463979/   -  person Z80    schedule 16.12.2010


Ответы (3)


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

Разница, которую вы наблюдаете, - это накладные расходы:

  • структуры, которые FastMM использует для отслеживания использования памяти,
  • куски памяти, которые FastMM еще не вернул в управление памятью Windows для оптимизации аналогичного распределения памяти в будущем.
person Jeroen Wiert Pluimers    schedule 15.12.2010
comment
Не могли бы вы прокомментировать, пожалуйста, Inside - значение Windows - person Branko; 09.03.2012
comment
@Branko, пожалуйста, объясните свой комментарий, так как я не понимаю, чего вы от меня ожидаете. - person Jeroen Wiert Pluimers; 09.03.2012
comment
@JeroenWiertPluimers, вероятно (просто предполагаю), он говорит о возвращении приложения TestMemory OP. Inside ... Windows 3.637.248 - person EMBarbosa; 09.03.2012
comment
@JeroenWiertPluimers - Для FastMem разница между значениями Inside и Before составляет 1.048.768, а для Windows всего 4.096? - person Branko; 25.03.2012
comment
@Branko, по-видимому, большая часть вашей программы использует кучу Delphi (предоставленную FastMM) и почти ничего не использует кучу Windows. Это хорошо, поскольку именно этого вы обычно ожидаете от кода Delphi. - person Jeroen Wiert Pluimers; 25.03.2012

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

person Optimal Cynic    schedule 15.12.2010
comment
Например что? Если процесс освобождает рабочий большой объем данных из памяти, то нет причин хранить ссылки или что-то еще, поэтому я бы сказал, что в идеале размер памяти должен быть таким же, как до выполнения. - person ; 15.12.2010
comment
В идеале зависит от того, предпочитаете ли вы скорость размеру. - person Lars Truijens; 15.12.2010
comment
И ум также зависит от контекста. Чрезвычайно умный менеджер памяти может как ускорить, так и уменьшить объем работы большого сложного многопоточного приложения за счет потери памяти при выделении, а затем немедленном освобождении. Я недостаточно знаю о диспетчере памяти Delphi, чтобы знать, что именно он делает, но я знаю, что вы не можете экстраполировать пример в вопросе на любое приложение разумного размера. - person Optimal Cynic; 17.12.2010
comment
Между прочим, умный иногда может быть синонимом глупого в правильном (неправильном?) контексте. - person Optimal Cynic; 17.12.2010

Как работает getmem/malloc/free?

Распределитель кучи — используется malloc...

1) Внутренне выделяет большие куски (обычно от 64 КБ до 1 мегабайта) памяти, а затем подразделяет куски, чтобы дать вам 100-байтовые и 200-байтовые объекты и строки в программе. Когда вы освобождаете память, все, что происходит, это место, из которого она была выделена во внутреннем буфере, или фрагмент, который затем помечается как свободный. НА САМОМ ДЕЛЕ НИЧЕГО НЕ ПРОИСХОДИТ!

2) Таким образом, вы можете думать о HEAP как о списке больших фрагментов памяти, а все объекты в вашей программе — это просто маленькие части этих фрагментов.

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

Это довольно наивное описание системы кучи, но большинство куч работают схожим образом, но выполняют гораздо большую оптимизацию. Но ваш вопрос в том, почему память не уменьшается, а ответ в том, что на самом деле ничего не освобождается. Внутренние страницы памяти сохраняются для следующего вызова «new» или «malloc» и т. д.

ПРЕДСТАВЬТЕ ЭТО

ВНУТРИ КУЧИ ЭТО ОДИН ОГРОМНЫЙ БЛОК 100 КБ

You call "malloc(1000)" or "getmem(1000)" to get a 1K block of memory.

Затем все, что происходит, это то, что блок памяти размером 1 КБ берется из блока памяти размером 100 КБ, оставляя 99 КБ памяти в этом блоке. Если вы продолжаете вызывать malloc или getmem, то он просто будет продолжать делить больший блок до тех пор, пока ему не понадобится другой больший блок.

Каждый небольшой блок памяти, выделенный вызовом malloc или getmem, фактически получает около 16 или 24 дополнительных байта (в зависимости от распределителя) дополнительной памяти. Эта память представляет собой биты, которые распределитель использует, чтобы узнать, что выделено, а также где оно выделено.

person Community    schedule 12.02.2013