如何说服内存管理器释放未使用的内存

时间:2010-12-17 23:14:44

标签: delphi fastmm

在最近的一篇文章(My program never releases the memory back. Why?)中,我展示了使用FastMM时,应用程序不会将大量内存释放回系统。 最近我创建了一个人工测试程序,以确保问题不是内存,而且只出现在FastMM中。

在这个程序中,我创建并销毁一个对象(与前一篇文章中使用的对象相同)500次。

内存要求为(“私人工作集”):

没有FastMM
在运行循环之前:1.2MB
运行循环后:2.1MB

使用FastMM(激进的调试模式)
在运行循环之前:2.1MB
运行循环后:25MB

使用FastMM(发布模式)
在运行循环之前:1.8MB
运行循环后:3MB

如果我多次运行循环,则内存要求不会增加。这意味着重用未释放的内存,因此这不是内存泄漏(内存泄漏会增加内存占用,每次运行时会有几KB / MB)。


我的问题是:

如何在FastMM中禁用此行为?它甚至可能吗?我知道,如果我在没有FastMM或FastMM Release Mode的情况下发布程序,它将“浪费”适量的RAM。但是,根据需要禁用此行为,将帮助我(我们?)识别内存泄漏。实际上在我的第一篇文章中(见链接),很多人都认为我有泄漏。由于这种行为,显然造成了混乱。不,很明显没有泄漏。只是内存管理器拒绝释放大量内存。

它会释放额外的内存吗?什么时候?什么引发了这个?程序员可以触发吗?例如,当我知道我已经完成了RAM密集型任务并且用户可能暂时不使用该程序(最小化它)时,我可以将RAM刷回系统吗?当用户打开我的程序的多个实例时会发生什么?他们不会争夺RAM吗?

4 个答案:

答案 0 :(得分:11)

你不应该把它视为“浪费”RAM,真的。将其视为“缓存”未使用的RAM。内存管理器正在抓住未使用的内存,而不是因为某种原因将其释放回操作系统,事实上你已经在问题中找到了这个原因。

你说你继续在循环中重新运行相同的操作。当你这样做时,它仍然有可用的旧内存,它可以立即分配它,而不必向Windows请求一大堆新的堆。这是将“Fast”置于“FastMM”中的技巧之一,如果它没有这样做,你会发现你的程序运行得慢得多。

您无需担心FastMM调试模式。这只是用于调试,并且您不会发布针对FullDebugMode编译的程序。并且“不使用FastMM”和“使用FastMM发布模式”之间的差异大约为1 MB,这在现代硬件上可以忽略不计。对于只需1 MB的低成本,您可以获得巨大的性能提升。所以不要担心。

答案 1 :(得分:9)

使FastMM快速发展的一部分原因是它将分配一大块内存并从中分割出更小的统一大小的内存。如果正在使用块的任何部分,则不能将其全部释放回操作系统。

欢迎您使用其他内存管理器。一种方法是将所有分配直接路由到VirtualAlloc。分配将被四舍五入以一次占用整个页面,因此如果您有大量小分配,您的程序可能会受到影响,但是当您致电VirtualFree时,您可以确信内存肯定不属于你的课程了。

另一种选择是将所有内容路由到操作系统堆。使用HeapAlloc。您甚至可以为您的程序启用the low-fragmentation heap(默认情况下从Windows Vista开始),这将使操作系统采用类似于FastMM使用的策略,但它将允许您使用一些调试和分析工具从Microsoft跟踪程序的内存使用情况。但请注意,在您致电HeapFree后,某些指标可能仍会将内存显示为属于您的计划。

此外,工作集指的是当前在物理RAM 中的内存。你观察到这个数字上升并不意味着你的程序已经分配了更多的内存。它可以简单地表示您的程序触及先前已分配但尚未放入RAM的内存。在循环期间,您触摸了该内存,操作系统尚未决定将其重新登录到磁盘。

答案 2 :(得分:3)

我使用以下内容作为内存管理器。我这样做是因为它在线程争用下比FastMM表现得更好,而实际上相当差。我知道像Hoard这样的可扩展管理器会更好,但这可以满足我的需求。

unit msvcrtMM;

interface

implementation

type
  size_t = Cardinal;

const
  msvcrtDLL = 'msvcrt.dll';

function malloc(Size: size_t): Pointer; cdecl; external msvcrtDLL;
function realloc(P: Pointer; Size: size_t): Pointer; cdecl; external msvcrtDLL;
procedure free(P: Pointer); cdecl; external msvcrtDLL;

function GetMem(Size: Integer): Pointer;
begin
  Result := malloc(size);
end;

function FreeMem(P: Pointer): Integer;
begin
  free(P);
  Result := 0;
end;

function ReallocMem(P: Pointer; Size: Integer): Pointer;
begin
  Result := realloc(P, Size);
end;

function AllocMem(Size: Cardinal): Pointer;
begin
  Result := GetMem(Size);
  if Assigned(Result) then begin
    FillChar(Result^, Size, 0);
  end;
end;

function RegisterUnregisterExpectedMemoryLeak(P: Pointer): Boolean;
begin
  Result := False;
end;

const
  MemoryManager: TMemoryManagerEx = (
    GetMem: GetMem;
    FreeMem: FreeMem;
    ReallocMem: ReallocMem;
    AllocMem: AllocMem;
    RegisterExpectedMemoryLeak: RegisterUnregisterExpectedMemoryLeak;
    UnregisterExpectedMemoryLeak: RegisterUnregisterExpectedMemoryLeak
  );

initialization
  SetMemoryManager(MemoryManager);

end.

这不是你的问题的答案,但它太长,无法适应评论,你可能会发现对这个MM运行你的应用程序很有意思。我的猜测是它的运行方式与FastMM相同。

答案 3 :(得分:1)

<强>解决

根据Barry Kelly的建议,记忆将由FastaMM自动发布。 为了确认这个,我创建了第二个分配了大量RAM的程序。一旦Windows用完RAM,我的程序内存利用率就恢复到原来的值。

问题解决了。 谢谢巴里。