注意:32位应用程序,不打算迁移到64位。
我正在使用一个非常消耗内存的应用程序,并且已经在内存分配/释放方面优化了所有相关路径。(据我所知和测试,应用程序本身没有任何内存泄漏、句柄泄漏或其他类型的泄漏。当然,我无法操作的第三方库是可能的,但在我的情况下不太可能出现这种情况)
该应用程序将经常分配大型单维和双维动态数组,其中包含多达4个浮点数的单个和压缩记录。通过大型我指的是5000x5000的记录(单,单,单,单)是正常的。同时在工作中甚至有6到7个这样的数组。这是必要的,因为在这些数组上进行了许多交叉计算,如果从磁盘读取它们会导致性能严重下降。
在澄清了这一点之后,由于这些大型动态数组不会在释放后消失,即使将它们设置为0或完成它们,我经常会遇到内存不足错误。我知道这当然是FastMM为了速度而做的事情。
我使用以下方法跟踪FastMM分配的块和进程消耗的内存(RAM + PF):
我在一台64位电脑上运行代码,崩溃前的最高内存消耗约为3.3-3.4 GB。之后,在应用程序的任何位置都会出现与内存/资源相关的崩溃。我花了一些时间才将其锁定在大型动态数组使用上,这些数组被深深地嵌入到某些第三方库中。
我采取的解决方法是通过重新启动应用程序并关闭带有特定参数的应用程序来使应用程序从上次离开的地方恢复自身。如果内存消耗合理且当前操作完成,则这很好。但当当前内存使用量为1GB,而要处理的下一个操作需要2.5GB或更多的内存时,就会出现大问题。我的当前代码将自身限制在使用1.5GB内存之前进行恢复,但在这种情况下,我必须将限制降低到1GB以下,这基本上意味着应用程序在每个操作之后都会恢复自身,即使如此也不能保证一切都会顺利。
如果另一个操作需要处理更大的数据集,并且需要总共4GB或更多的内存,怎么办?
请注意,我所说的不是实际内存中的4GB,而是通过分配巨大的动态数组消耗的内存,操作系统在解除分配后无法获取回来,因此仍然将其视为已使用的内存,所以会增加内存消耗。
因此,我的下一个攻击点是强制FastMM释放所有(或至少部分)内存给操作系统。我特别针对巨大的动态数组进行操作。同样,这些数组在第三方库中,因此重新编码不是最佳选择。在FastMM代码中进行微调并编写过程以释放内存要容易得多且更快速。
我不能从FastMM切换,因为当前整个应用程序和一些第三方库都是围绕使用PushAllocationGroup编写的,以便快速查找和定位任何内存泄漏。我知道我可以编写一个虚拟的FastMM单元来解决编译引用问题,但我将没有这种快速而确定的泄漏检测功能。
总之:有没有办法强制FastMM释放其至少一部分大块到操作系统?(好吧,当然有,真正的问题是:是否有人写过它,如果是,请分享?)
谢谢
稍后编辑:
我很快就会提供一个小的相关测试应用程序。看起来模拟一个应用程序并不容易。
我正在使用一个非常消耗内存的应用程序,并且已经在内存分配/释放方面优化了所有相关路径。(据我所知和测试,应用程序本身没有任何内存泄漏、句柄泄漏或其他类型的泄漏。当然,我无法操作的第三方库是可能的,但在我的情况下不太可能出现这种情况)
该应用程序将经常分配大型单维和双维动态数组,其中包含多达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;
我在一台64位电脑上运行代码,崩溃前的最高内存消耗约为3.3-3.4 GB。之后,在应用程序的任何位置都会出现与内存/资源相关的崩溃。我花了一些时间才将其锁定在大型动态数组使用上,这些数组被深深地嵌入到某些第三方库中。
我采取的解决方法是通过重新启动应用程序并关闭带有特定参数的应用程序来使应用程序从上次离开的地方恢复自身。如果内存消耗合理且当前操作完成,则这很好。但当当前内存使用量为1GB,而要处理的下一个操作需要2.5GB或更多的内存时,就会出现大问题。我的当前代码将自身限制在使用1.5GB内存之前进行恢复,但在这种情况下,我必须将限制降低到1GB以下,这基本上意味着应用程序在每个操作之后都会恢复自身,即使如此也不能保证一切都会顺利。
如果另一个操作需要处理更大的数据集,并且需要总共4GB或更多的内存,怎么办?
请注意,我所说的不是实际内存中的4GB,而是通过分配巨大的动态数组消耗的内存,操作系统在解除分配后无法获取回来,因此仍然将其视为已使用的内存,所以会增加内存消耗。
因此,我的下一个攻击点是强制FastMM释放所有(或至少部分)内存给操作系统。我特别针对巨大的动态数组进行操作。同样,这些数组在第三方库中,因此重新编码不是最佳选择。在FastMM代码中进行微调并编写过程以释放内存要容易得多且更快速。
我不能从FastMM切换,因为当前整个应用程序和一些第三方库都是围绕使用PushAllocationGroup编写的,以便快速查找和定位任何内存泄漏。我知道我可以编写一个虚拟的FastMM单元来解决编译引用问题,但我将没有这种快速而确定的泄漏检测功能。
总之:有没有办法强制FastMM释放其至少一部分大块到操作系统?(好吧,当然有,真正的问题是:是否有人写过它,如果是,请分享?)
谢谢
稍后编辑:
我很快就会提供一个小的相关测试应用程序。看起来模拟一个应用程序并不容易。
result := 0
那么LastRead := result
就会读取一个未初始化的变量。 - David Heffernan