Защо използването на паметта на програмата ми не се връща към нормалното, след като освободя памет?

разгледайте следващото примерно приложение

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 - наистина ли е умно? Изглежда, че приложението ми не връща около 160MB. На компютри с 1 GB или RAM (или по-малко) това не е ли загуба на RAM? Подробности: stackoverflow. com/questions/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 value - 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 Heap (предоставен от FastMM) и почти нищо Windows heap. Това е добре, тъй като това е, което обикновено очаквате от кода на 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 K до 1 мегабайт) памет и след това подразделя парчетата нагоре, за да ви даде 100-байтови и 200-байтови обекти и низове в програмата. Когато освободите памет, всичко, което се случва, е мястото, от което е разпределена във вътрешния буфер или част, след което се маркира като свободна. ВСЪЩНОСТ НИЩО НЕ СЕ СЛУЧВА!

2) Така че можете да мислите за HEAP като списък от големи парчета памет и всички обекти във вашата програма са само малки части от тези парчета.

3) Големите вътрешни части от паметта се освобождават само когато всички обекти в тях са освободени, така че обичайният случай е, че когато освободите някакъв обект, нищо не се случва, освен някои битове да бъдат маркирани като налични.

Това е доста наивно описание на системата за купчина, но повечето купчини работят по подобен начин, но правят много повече оптимизация от това. Но вашият въпрос е защо паметта не намалява и отговорът е, защото всъщност нищо не се освобождава. Вътрешните страници на паметта се запазват за следващото извикване на "new" или "malloc" и т.н.

ПРЕДСТАВЕТЕ СИ ГО

В ХИПЪТ Е ЕДИН ОГРОМЕН БЛОК ОТ 100 Kb

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

Тогава всичко, което се случва, е, че 1K блок памет се взема от 100kb блок памет, оставяйки 99K памет налична в този блок. Ако продължите да извиквате malloc или getmem, той просто ще продължи да разделя по-големия блок, докато не се нуждае от друг по-голям блок.

Всеки малък блок памет, разпределен с извикване на malloc или getmem, всъщност получава около 16 или 24 допълнителни байта (в зависимост от разпределителя) допълнителна памет. Тази памет е битове, които разпределителят използва, за да знае какво е разпределено и също къде е разпределено.

person Community    schedule 12.02.2013