5

在最近的一篇文章中(我的程序从不释放内存。为什么?)我展示了在使用 FastMM 时,应用程序不会将大量内存释放回系统。最近我创建了一个人工测试程序,以确保它不是内存问题,并且它只出现在 FastMM 中。

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

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

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

使用 FastMM(积极调试模式)
运行循环前:2.1MB
运行循环后:25MB

使用 FastMM(释放模式)
运行循环前:1.8MB
运行循环后:3MB

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


我的问题是:

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

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

4

4 回答 4

11

真的,您不应该将其视为“浪费” RAM。将其视为“缓存”未使用的 RAM。内存管理器保留未使用的内存而不是出于某种原因将其释放回操作系统,实际上您已经在问题中遇到了这个原因。

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

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

于 2010-12-17T23:35:50.557 回答
9

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

欢迎您使用不同的内存管理器。一种方法是将所有分配直接路由到VirtualAlloc. 分配将被四舍五入一次占据整个页面,因此如果您有很多小分配,您的程序可能会受到影响,但是当您调用 时VirtualFree,您可以确信内存绝对不再属于您的程序。

另一种选择是将所有内容路由到操作系统堆。使用HeapAlloc. 您甚至可以为您的程序启用低碎片堆(默认情况下在 Windows Vista 中启用),这将使操作系统采用类似于 FastMM 使用的策略,但它允许您使用一些调试和分析工具从 Microsoft 跟踪您的程序随时间推移的内存使用情况。但请注意,在您调用 之后HeapFree,某些指标可能仍会将内存显示为属于您的程序。

此外,工作集是指当前在物理 RAM 中的内存。您观察到数字上升并不意味着您的程序分配了更多内存。这可能只是意味着您的程序触及了一些它之前分配但尚未放入 RAM 的内存。在您的循环中,您触及了该内存,而操作系统尚未决定将其分页回磁盘。

于 2010-12-18T01:11:36.887 回答
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 相同。

于 2010-12-18T11:10:34.033 回答
1

解决了

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

问题解决了。谢谢巴里。

于 2010-12-27T16:47:40.573 回答