delphi – 如何解决内存分段并强制FastMM向OS释放内存?
注意:32位应用程序,不计划迁移到64位.
我正在使用一个非常耗费内存的应用程序,并且几乎已经针对内存分配/解除分配优化了所有相关路径. (没有内存泄漏,没有句柄泄漏,应用程序本身没有任何其他类型的泄漏AFAIK并且经过测试.我无法触及的第三方库当然是候选者,但在我的场景中不太可能) 该应用程序将经常分配单个和多维记录的单个和二维动态数组,最多4个单一.我的意思是5000×5000的记录(单,单,单)是正常的.在给定时间内甚至还有6或7个这样的阵列工作.这是必需的,因为在这些阵列上进行了大量的交叉计算,并且从磁盘读取它们将是真正的性能杀手. 有了这个澄清之后,由于这些大型动态数组在释放它们后不会消失,无论我将它们设置为0还是最终确定它们,我都会出现内存错误.这当然是FastMM为了快速而做的事情,我知道的很多. 我正在使用以下方法跟踪FastMM分配的块和处理消耗的内存(RAM PF): function CurrentProcessMemory(AWaitForConsistentRead:boolean): Cardinal; var MemCounters: TProcessMemoryCounters; LastRead:Cardinal; maxCnt:integer; begin result := 0;// stupid D2010 compiler warning maxCnt := 0; repeat Inc(maxCnt); // this is a stabilization loop; // in tight loops,the system doesn't get // much chance to release allocated resources,which in turn will get falsely // reported by this function as still being used,resulting in a false-positive // memory leak report in the application. // so we do a tight loop here,waiting,until the application reported memory // gets stable. LastRead := result; MemCounters.cb := SizeOf(MemCounters); if GetProcessMemoryInfo(GetCurrentProcess,@MemCounters,SizeOf(MemCounters)) then Result := MemCounters.WorkingSetSize + MemCounters.PagefileUsage else RaiseLastOSError; if AWaitForConsistentRead and (LastRead <> 0) and (abs(LastRead - result)>1024) then begin sleep(60); application.processmessages; end; until (not AWaitForConsistentRead) or (abs(LastRead - result)<1024) or (maxCnt>1000); // 60 seconds wait is a bit too much // so if the system is that "unstable",let's just forget it. end; function CurrentFastMMMemory:Cardinal; var mem:TMemoryManagerUsageSummary; begin GetMemoryManagerUsageSummary(mem); result := mem.AllocatedBytes + mem.OverheadBytes; end; 我在64位计算机上运行代码,在崩溃之前我的最高内存消耗大约是3.3 – 3.4 GB.之后,我在应用程序的任何位置获得与内存/资源相关的崩溃.花了我一些时间来介绍一些第三方库中埋藏的大型动态数据使用情况. 我克服这个问题的方法是,我通过重新启动自己并使用某些参数关闭来使应用程序从它停止的地方恢复. 当前内存使用量为1GB且下一个要处理的操作需要处理2.5 GB或更多内存时,会出现一个大问题.我的当前代码在恢复之前将自身限制为1.5 GB内存的上限值,但在这种情况下,我必须将限制降低到1 GB以下,这基本上会使应用程序在每次操作后自行恢复,甚至不保证一切都会好的. 如果另一个操作将有更大的数据集要处理并且它将需要总共4GB或更多内存,该怎么办? 要注意的是,我不是在讨论内存中实际的4 GB,而是通过分配巨大的动态数组来消耗内存,一旦取消分配,操作系统就不会回来,因此它仍然将其视为已消耗,因此它会增加. 因此,我的下一个攻击点是强制fastmm释放所有(或至少部分)内存到操作系统.我专门针对这里庞大的动态数组.同样,这些都在第三方库中,所以重新编码并不是真正的顶级选项.修改fastmm代码并编写proc来释放内存变得更加容易和快捷. 我无法从FastMM切换为当前整个应用程序,并且一些第三方库在使用PushAllocationGroup时进行了大量编码,以便快速查找并查明任何内存泄漏.我知道我可以编写一个虚拟的FastMM单元来解决编译参考,但是如果没有这种快速和确定的泄漏检测,我将会离开. 总结:有什么方法可以强制FastMM向操作系统发布至少一些大块? (当然,确实存在,实际的问题是:是否有人写过它,如果有的话,心灵分享?) 谢谢 稍后编辑: 我很快就会提出一个小的相关测试应用程序.模拟一个似乎并不容易 解决方法
我怀疑这个问题实际上归结为FastMM.对于巨大的内存块,FastMM不会进行任何子分配.您的分配请求将使用直接VirtualAlloc处理.然后释放是VirtualFree.
假设您在一个连续的块中分配这些380MB对象.我怀疑你实际拥有的是粗糙的2D动态数组.而且它们不是单一的分配.一个5000×5000参差不齐的2D动态阵列需要5001个分配才能初始化.一个用于行指针,5000用于行.那些将是中等FastMM块.会有分配. 我觉得你问的太多了.根据我的经验,任何时候你需要在32位进程中超过3GB的内存,它的游戏结束.在内存不足之前,地址空间的碎片将阻止您.你不能希望这个工作.切换到64位,或使用更聪明,要求更低的分配模式.或者您真的需要密集的2D阵列吗?你可以使用稀疏存储吗? 如果无法通过这种方式减轻内存需求,则可以使用内存映射文件.这将允许您使用64位系统具有的额外内存.系统的磁盘缓存可能大于4GB,因此您的应用程序可以遍历超过4GB的内存而无需实际需要访问磁盘. 你当然可以尝试不同的内存管理器.老实说,我不抱任何希望它会有所帮助.您可以编写一个使用HeapAlloc的简单替换内存管理器.并启用低碎片堆(默认情况下从Vista启用).但我真诚地怀疑它会有所帮助.我担心你不会有快速解决方案.要解决此问题,您需要对代码进行更基本的修改. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |