可以清理记忆吗?

Tom*_*m A 30 delphi garbage-collection memory-management

我在Win32项目中使用Delphi 5(安装了FastMM),并且最近一直在尝试大幅减少此应用程序中的内存使用量.到目前为止,我已将使用量减少了近一半,但在处理单独的任务时发现了一些问题.当我最小化应用程序时,内存使用量从45兆位缩减到1兆位,这归功于它分页到磁盘.当我恢复并重新开始工作时,内存仅增加到15兆.当我继续工作时,内存使用量再次缓慢上升,最小化和恢复将其恢复到15兆.所以我的想法是,当我的代码告诉系统释放内存时,根据Windows仍然保留它,实际的垃圾收集直到很久以后才开始.

任何人都可以确认/否认这种行为吗?是否有可能以编程方式清理内存?如果我继续使用该程序而不执行此手动刷新,我会在一段时间后出现内存不足错误,并希望消除它.谢谢.

编辑:我在about.com上发现了一篇文章,其中提供了很多内容以及其他内存管理领域的链接和数据.

Bar*_*lly 73

任务管理器不显示应用程序从Windows分配的总数.它显示的内容(默认情况下)是工作集.工作集是一个旨在尝试在内存受限条件下尽量减少页面文件抖动的概念.它基本上是应用程序定期触摸的内存中的所有页面,因此为了使该应用程序以良好的响应性运行,操作系统将努力将工作集保持在物理内存中.

根据用户不太关心最小化应用程序响应性的理论,操作系统会修剪其工作集.这意味着,在物理内存压力下,该进程拥有的虚拟内存页面更有可能被分页到磁盘(到页面文件)以腾出空间.

大多数现代系统在大多数情况下都没有针对大多数应用程序的分页问题.严重的页面抖动机器几乎与崩溃的机器无法区分,在应用程序响应用户输入之前需要几秒甚至几分钟.

因此,您看到的行为是Windows在最小化时修剪工作集,然后随着应用程序的恢复,随着时间的推移将其恢复,从而触及越来越多的页面.这与垃圾收集完全不同.

如果您对Windows下的应用程序的内存使用感兴趣,则没有一个最重要的数字,而是一系列相关数字:

  • 虚拟大小 - 这是应用程序保留的地址空间总量.地址空间(即指针指向的地址)可以是未保留的,保留的或提交的.未来可以通过内存管理器或通过加载DLL(DLL必须在内存中的某个位置)等分配未预留的内存.

  • 私有工作集 - 这是该应用程序专用的页面(即不在多个正在运行的应用程序之间共享,以便所有人都可以看到对一个应用程序的更改),并且是工作集的一部分(即,经常被应用程序).

  • 可共享工作集 - 这是工作集中可共享的页面,但实际上可能会或可能不会共享.例如,DLL或包(BPL)可以加载到应用程序的存储空间中.这些DLL的代码可能会在多个进程之间共享,但如果DLL只加载一次到一个应用程序中,那么它实际上并不是共享的.如果DLL高度特定于此应用程序,则它在功能上等同于私有工作集.

  • 共享工作集 - 这是工作集中实际共享的页面.可以将任何一个应用程序的这些页面的"成本"归因于共享的数量除以共享页面的应用程序的数量.

  • 专用字节 - 这是来自虚拟地址空间的页面,这些页面由此应用程序提交,并且不在应用程序之间共享(或共享).几乎每个应用程序内存管理器的内存分配都会在此池中结束.只有在某些频率下使用的页面才能成为工作集的一部分,因此这个数字通常比私有工作集大.稳定增加的专用字节数表示内存泄漏或具有大空间要求的长时间运行算法.

这些数字不代表不相交的集合.它们是总结不同类型页面状态的不同方式.例如,工作集 =私人工作集+可共享工作集.

这些数字中哪一个最重要取决于您受约束的内容.如果您尝试使用内存映射文件执行I/O,则虚拟大小将限制您可以将多少内存用于映射.如果您处于物理内存受限的环境中,则希望最小化工作集.如果同时运行许多不同的应用程序实例,则需要最小化专用字节并最大化共享字节.如果您正在生成一堆不同的DLL和BPL,您需要确保它们实际上是共享的,确保它们的加载地址不会导致它们发生冲突并阻止共享.

关于SetProcessWorkingSetSize:

Windows通常根据内存压力自动处理工作集.工作集不会确定您是否会遇到内存不足(OOM)错误.工作集用于做出有关分页的决定,即保留在内存中的内容以及磁盘上的内容(在DLL的情况下)或页面输出到磁盘(其他已提交的内存).除非分配的虚拟内存多于系统中的物理内存,否则它不会产生任何影响.

至于其效果:如果下限设置为高,则意味着该过程将对其他应用程序产生敌意,并在物理内存压力的情况下尝试占用内存.这是它需要安全权限PROCESS_SET_QUOTA的原因之一.

如果将上限设置为低,则意味着Windows不会努力将页面保留在此应用程序的物理内存中,并且当物理内存压力变高时,Windows可能会将大部分内容分页到磁盘.

在大多数情况下,您不希望更改工作集详细信息.通常最好让操作系统处理它.它不会阻止OOM情况.这些通常是由地址空间耗尽引起的,因为内存管理器无法提交更多内存; 当页面文件中的空间用完时,或者在页面文件空间不足的系统中备份已提交的虚拟内存.

  • 巴里,你有解释复杂话题的真正诀窍.你应该把这个发布在你的博客上,这样我们就可以在将来询问过程记忆的时候引用人们.天知道这样的问题就足够了! (15认同)

gab*_*abr 17

这就是我们在DSiWin32中使用的内容:

procedure DSiTrimWorkingSet;
var
  hProcess: THandle;
begin
  hProcess := OpenProcess(PROCESS_SET_QUOTA, false, GetCurrentProcessId);
  try
    SetProcessWorkingSetSize(hProcess, $FFFFFFFF, $FFFFFFFF);
  finally CloseHandle(hProcess); end;
end; { DSiTrimWorkingSet }
Run Code Online (Sandbox Code Playgroud)

  • 小更新:已经在代码中使用了这个工作超过一周,我们现在平均运行5兆内存和40兆内存虚拟内存从75和75开始. (2认同)

Fra*_*ois 5

让我们直截了当: FastMM4不会泄漏内存,您的代码可能会泄漏.

要确切知道,请在应用程序的某个位置执行此指令(其中FastMM4位于uses子句中,并$define ManualLeakReportingControl在FastMM4Options.inc中设置):

ReportMemoryLeaksOnShutdown := True;
Run Code Online (Sandbox Code Playgroud)

如果忘记释放一些内存,FastMM4最后会报告.

如果您想了解更多,可以观看CodeRage 2中的视频:战斗傻瓜的内存泄漏