【问题标题】:Why doesn't my program's memory usage return to normal after I free memory?为什么我释放内存后程序的内存使用率没有恢复正常?
【发布时间】:2011-05-25 18:57:46
【问题描述】:

考虑下一个示例应用程序

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

我想知道为什么BeforeAfter的内存使用结果不同:

【问题讨论】:

  • @Optimal Cynic - 真的很聪明吗?似乎我的应用程序没有返回大约 160MB。在具有 1GB 或 RAM(或更少)的计算机上,这不是浪费 RAM 吗?详情:stackoverflow.com/questions/4463979/…

标签: delphi delphi-2007


【解决方案1】:

任何内存管理器(包括 FastMM)都会产生一些开销,否则 Delphi 可能只使用 Windows 内存管理器。

您观察到的差异是开销:

  • FastMM 用于跟踪内存使用情况的结构,
  • FastMM 尚未返回到 Windows 内存管理的内存块以优化未来类似的内存分配。

【讨论】:

  • 您能否评论一下“Inside - Windows”值
  • @Branko 请解释您的评论,因为我不明白您对我的期望。
  • @JeroenWiertPluimers 可能(只是猜测)他在谈论 OP 的 TestMemory 应用程序的回归。 Inside ... Windows 3.637.248
  • @JeroenWiertPluimers - 对于 FastMem,Inside 和 Before 值之间的差异是 1.048.768,但对于 Windows,它只有 4.096?
  • @Branko 显然您的大部分程序都在使用 Delphi 堆(由 FastMM 提供),而几乎没有使用 Windows 堆。这很好,因为这是您通常对 Delphi 代码的期望。
【解决方案2】:

因为内存管理器在后台做一些聪明的事情来提高性能。

【讨论】:

  • 例如什么?如果进程从内存中释放大量工作数据,那么就没有理由保留引用或其他任何东西,所以我想说,理想情况下,内存大小应该与执行前相同。
  • 理想情况下取决于您是否更喜欢速度而不是大小。
  • 聪明也取决于上下文。一个非常聪明的内存管理器可能会加快和减少大型复杂多线程应用程序的占用空间 - 代价是在“分配,然后立即解除分配”的情况下浪费内存。我对 Delphi 的内存管理器知之甚少,不知道它到底在做什么,但我知道你不能从问题中的示例推断任何合理大小的应用程序。
  • 顺便说一句,在正确的(错误的?)上下文中,聪明有时可能是愚蠢的同义词。
【解决方案3】:

getmem/malloc/free 是如何工作的?

堆分配器 - 由 malloc 使用...

1) 在内部分配大块内存(通常为 64K 到 1Megabyte),然后将块细分为程序中的 100byte 和 200byte 对象和字符串。 当您释放内存时,所有发生的事情都是从内部缓冲区或块中分配它的位置,然后将其标记为空闲。什么都没有发生!

2) 因此,您可以将 HEAP 视为一大块内存的列表,而您程序中的所有对象都只是这些内存块的一小部分。

3) 只有当其中的所有对象都已被释放时,才会释放大的内部内存块,因此通常的情况是,当您释放某个对象时,除了某些位被标记为可用之外,实际上什么都没有发生。

这是对堆系统的相当幼稚的描述,但大多数堆以类似的方式工作,但做了更多的优化。但是你的问题是为什么内存没有下降,答案是因为实际上没有任何东西被释放。内存的内部页面被保留以供下次调用“new”或“malloc”等......

想象一下

堆内部是一个 100Kb 的大块

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

然后所有发生的事情就是从 100kb 的内存块中取出 1K 的内存块,在该块中留下 99K 的可用内存。如果你继续调用 malloc 或 getmem,那么它只会继续分割更大的块,直到它需要另一个更大的块。

通过调用 malloc 或 getmem 分配的每个小块内存实际上会获得大约 16 或 24 个额外字节(取决于分配器)额外内存。该内存是分配器用来了解分配的内容以及分配位置的位。

【讨论】:

    猜你喜欢
    • 2015-06-14
    • 1970-01-01
    • 1970-01-01
    • 2010-10-01
    • 1970-01-01
    • 2016-07-19
    • 2012-10-10
    • 1970-01-01
    相关资源
    最近更新 更多