ciu*_*uly 5 memory delphi memory-management out-of-memory fastmm
注意:32位应用程序,不计划迁移到64位.
我正在使用一个非常耗费内存的应用程序,并且几乎已经针对内存分配/解除分配优化了所有相关路径.(没有内存泄漏,没有句柄泄漏,应用程序本身没有任何其他类型的泄漏AFAIK并且经过测试.我无法触及的第三方库当然是候选者,但在我的场景中不太可能)
该应用程序将经常分配单个和多维记录的单个和二维动态数组,最多4个单一.我的意思是5000x5000的记录(单,单,单,单)是正常的.在给定时间内甚至还有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;
Run Code Online (Sandbox Code Playgroud)
我在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 的对象分配在一个连续的块中。我怀疑你实际上拥有的是不规则的二维动态数组。而且它们不是单一分配。5000x5000 不规则的二维动态数组需要 5001 次分配来初始化。1 个用于行指针,5000 个用于行。这些将是中等 FastMM 块。会有二次分配。
我认为你要求太多了。根据我的经验,只要 32 位进程需要超过 3GB 的内存,游戏就结束了。地址空间碎片会在内存耗尽之前阻止您。你不能指望这能起作用。切换到 64 位,或者使用更聪明、要求更低的分配模式。或者你真的需要密集的二维阵列吗?可以使用稀疏存储吗?
如果您无法通过这种方式缓解内存需求,则可以使用内存映射文件。这将允许您利用 64 位系统拥有的额外内存。系统的磁盘缓存可以大于 4GB,因此您的应用程序可以遍历超过 4GB 的内存,而无需实际访问磁盘。
您当然可以尝试不同的内存管理器。老实说,我不抱任何希望它会有所帮助。您可以编写一个简单的替换内存管理器,使用HeapAlloc
. 并启用低碎片堆(从 Vista 开始默认启用)。但我真诚地怀疑这是否会有帮助。恐怕没有快速的办法可以解决你的问题。要解决此问题,您需要对代码进行更根本的修改。
归档时间: |
|
查看次数: |
1888 次 |
最近记录: |