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

6
在最近的一篇文章中(我的程序为什么从不释放内存?),我展示了使用FastMM时,应用程序不会将大量内存释放回系统。最近,我创建了一个人工测试程序,以确保这个问题不是内存问题,并且只出现在FastMM中。
在这个程序中,我创建并销毁一个对象(与之前的帖子中使用的对象相同)500次。 内存要求为(“私有工作集”): 没有使用FastMM
循环运行之前:1.2MB
循环运行之后:2.1MB
使用FastMM(调试模式)
循环运行之前:2.1MB
循环运行之后:25MB
使用FastMM(发布模式)
循环运行之前:1.8MB
循环运行之后:3MB
如果我多次运行循环,内存需求不会增加。这意味着未释放的内存被重新使用,因此这不是内存泄漏(内存泄漏会在每次运行时增加几KB / MB的内存占用)。
我的问题是:
如何在FastMM中禁用此行为?是否可能?我知道,如果我使用FastMM Release Mode或不使用FastMM发布程序,它将“浪费”适度的RAM。但是,按需禁用此行为将帮助我(我们?)识别内存泄漏。实际上,在我的第一篇文章中(请参见链接),许多人建议我有一个泄漏。显然,这种行为引起了混乱。不,很明显没有泄漏。只是内存管理器拒绝释放大量内存。
它会释放额外的内存吗?什么时候?什么触发器?程序员可以触发它吗?例如,当我知道我完成了一个RAM密集型任务并且用户可能一段时间内不使用程序(最小化它)时,我可以将RAM刷新回系统吗?当用户打开我的程序的多个实例时会发生什么?他们不会争夺RAM吗?
4个回答

11
您不应该把它看作是“浪费”RAM,而应该将其视为“缓存”未使用的RAM。内存管理器保留未使用的内存而不是将其释放回给操作系统,这是有原因的,实际上在您的问题中就涉及到了这个原因。
您说自己在循环中不断重新运行相同的操作。当您这样做时,仍然可以使用旧的内存并立即进行分配,而不必请求Windows获取新的堆块。这是将“Fast”放入“FastMM”的技巧之一,如果没有这样做,您会发现程序运行得更慢。
您不需要担心FastMM调试模式的数字。那只用于调试,而您不会发布针对FullDebugMode编译的程序。而“无FastMM”和“有FastMM Release Mode”之间的差异大约为1 MB,在现代硬件上可以忽略不计。以仅增加1 MB的低成本,您将获得巨大的性能提升。所以不要担心。

嗨,梅森。"...保留未使用的内存而不将其释放回操作系统是有原因的"——天哪!我并不否认缓存的好处!!!我知道缓存很好!但是我希望能够对其进行一些控制——至少是为了调试目的。知道你的应用程序需要确切的内存量不是很好吗? :) - Gabriel
1
仅需额外的1MB成本,您就可以获得巨大的性能提升。这个数字是当我一次创建/销毁一个对象(500次)时“浪费”的RAM量。但是如果我加载500个对象然后释放它们,FastMM未释放的RAM数量非常惊人!Borland的内存管理器会立即返回大部分RAM!拥有1GB RAM EEEPC和Windows 7的用户, 即使第一个实例处于空闲状态而没有释放内存,也将无法启动我的程序的第二个实例。 - Gabriel

10
FastMM之所以快速,部分原因是它会分配一个大内存块并从中切割出更小、统一大小的块。如果该块的任何部分正在使用中,则无法将其释放回操作系统。
您可以选择使用不同的内存管理器。其中一种方法是直接将所有分配路由到VirtualAlloc。分配将被舍入为整个页面,因此如果您有大量小分配,则程序可能会受到影响,但是当您调用VirtualFree时,您可以确信该内存绝对不再属于您的程序。
另一种选择是将所有内容路由到操作系统堆中。使用HeapAlloc。您甚至可以为程序启用低碎片堆(自Windows Vista以来默认开启),这将使操作系统采用类似于FastMM使用的策略,但它将允许您使用Microsoft的某些调试和分析工具来跟踪程序随时间的内存使用情况。请注意,虽然在调用HeapFree之后,某些指标仍可能显示该内存属于您的程序。
此外,工作集是指当前在物理RAM中的内存。您观察到数字增加并不意味着您的程序已经分配了更多的内存。它只是表示您的程序触及了先前分配但尚未放入RAM中的一些内存。在循环期间,您触及了该内存,而操作系统尚未决定将其换出磁盘。

3

我使用以下内存管理器。我这样做是因为它在线程争用下的表现比FastMM要好得多,而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一样执行。


2

已解决

正如Barry Kelly所建议的那样,FastaMM会自动释放内存。 为了确认这一点,我创建了第二个程序来分配大量RAM。当Windows用尽RAM时,我的程序内存利用率恢复到原始值。

问题已解决。 谢谢Barry。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接