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

uyhoqukh  于 2023-10-18  发布在  其他
关注(0)|答案(4)|浏览(146)

在最近的一篇文章(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释放模式将“浪费”适量的RAM。但禁用这种行为的需求,将帮助我(我们?)识别内存泄漏。事实上,在我的第一篇文章(见链接)中,很多人认为我有一个漏洞。很明显,混乱就是因为这种行为造成的。不,很明显没有泄漏。只是内存管理器拒绝释放大量内存。
它会释放多余的内存吗?什么时候?什么触发了这个?程序员能触发它吗?例如,当我知道我已经完成了一个RAM密集型任务,用户可能暂时不会使用该程序(最小化它),我可以将RAM刷新回系统吗?当用户打开我的程序的多个示例时会发生什么?他们不会为RAM而竞争吗?

oxf4rvwz

oxf4rvwz1#

你不应该认为这是“浪费”RAM,真的。把它想象成“缓存”未使用的RAM。内存管理器持有未使用的内存而不是将其释放回操作系统是有原因的,事实上,您在问题中已经找到了这个原因。
你说你会在一个循环中重复运行相同的操作。当你这样做时,它仍然有旧的内存可用,它可以立即分配它,而不必向Windows请求新的堆块。这是将“Fast”置于“FastMM”中的技巧之一,如果它没有做到这一点,您会发现您的程序运行得更慢。
您不需要担心FastMM调试模式图。这只是为了调试,你不会发布一个针对FullslogMode编译的程序。而“不带FastMM”和“带FastMM发布模式”之间的差异约为1 MB,这在现代硬件上可以忽略不计。仅需额外增加1 MB的低成本,您就可以获得巨大的性能提升。所以别担心

bttbmeg0

bttbmeg02#

使FastMM快速的部分原因是它将分配一大块内存,并从中分割出较小的统一大小的块。如果块的任何部分正在使用,则不能将其释放回操作系统。
您可以使用不同的内存管理器。一种方法是将所有分配直接路由到VirtualAlloc。分配将被舍入到一次占用整个页面,所以如果你有很多小的分配,你的程序可能会受到影响,但是当你调用VirtualFree时,你可以确信内存肯定不再属于你的程序了。
另一种选择是将所有内容路由到OS堆。使用HeapAlloc。你甚至可以为你的程序启用the low-fragmentation heap(在Windows Vista中默认为启用),这将使操作系统采用类似于FastMM使用的策略,但它将允许你使用微软的一些调试和分析工具来跟踪你的程序随着时间的推移的内存使用情况。但是要注意,在调用HeapFree之后,某些指标可能仍然显示内存属于程序。
此外,* 工作集 * 指的是 * 当前在物理RAM* 中的内存。你观察到的数字上升并不意味着你的程序已经分配了更多的内存。它可以简单地意味着你的程序“触及”了一些它以前分配的内存,但这些内存还没有放入RAM。在循环过程中,您触及了该内存,而操作系统尚未决定将其页出到磁盘。

dffbzjpn

dffbzjpn3#

我使用以下代码作为内存管理器。我这样做是因为它在线程争用下的性能比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。

hsvhsicv

hsvhsicv4#

已解决

正如巴里凯利所建议的那样,FastaMM将自动释放内存。为了证实这一点,我创建了第二个程序,分配了大量的RAM。一旦Windows用完RAM,我的程序内存利用率就恢复到原来的值。
问题解决了谢谢,巴里。

相关问题