注意:32 位应用程序,不打算迁移到 64 位。
我正在使用一个非常消耗内存的应用程序,并且在内存分配/取消分配方面已经优化了所有相关路径。(没有内存泄漏,没有句柄泄漏,应用程序本身没有任何其他类型的泄漏 AFAIK 并经过测试。我无法触及的第 3 方库当然是候选库,但在我的场景中不太可能)
该应用程序将经常分配包含最多 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。之后,我在应用程序的任何地方都会遇到与内存/资源相关的崩溃。我花了一些时间来确定隐藏在某些 3rd 方库中的大型动态数组的使用情况。
我克服这个问题的方法是,我通过重新启动自身并使用某些参数关闭,使应用程序从它停止的地方恢复自身。如果内存消耗公平且当前操作完成,这一切都很好。
当当前内存使用量为 1GB 并且下一个要处理的操作需要 2.5 GB 或更多内存来处理时,就会出现大问题。我当前的代码在恢复之前将自身限制为 1.5 GB 已用内存的上限,但在这种情况下,我必须将限制降低到 1 GB 以下,这基本上会使应用程序在每次操作后自行恢复,甚至不能保证一切都会好起来的。
如果另一个操作需要处理更大的数据集并且总共需要 4GB 或更多内存怎么办?
需要注意的是,我不是在谈论实际的 4 GB 内存,而是通过分配巨大的动态数组来消耗内存,一旦取消分配,操作系统就不会返回,因此它仍然认为它已被消耗,所以它加起来。
因此,我的下一个攻击点是强制 fastmm 将所有(或至少部分)内存释放到操作系统。我专门针对这里的巨大动态数组。同样,这些都在第 3 方库中,因此重新编码并不是真正的首选。修改 fastmm 代码并编写 proc 来释放内存要容易和快捷得多。
我无法从 FastMM 切换,因为目前整个应用程序和一些第 3 方库都围绕 PushAllocationGroup 的使用进行了大量编码,以便快速查找和查明任何内存泄漏。我知道我可以编写一个虚拟的 FastMM 单元来解决编译引用问题,但是我将无法进行这种快速且确定的泄漏检测。
总之:有什么办法可以强制 FastMM 至少向操作系统释放一些大块?(嗯,当然有,实际的问题是:有没有人写过它,如果有,请分享一下?)
谢谢
稍后编辑:
我很快就会提出一个小的相关测试应用程序。模拟一个似乎并不容易