Delphi-检查内存是否正在释放;“准时”;

Delphi-检查内存是否正在释放;“准时”;,delphi,memory,out-of-memory,Delphi,Memory,Out Of Memory,我有一个没有内存泄漏的GUI应用程序。我已经在多次测试循环中用FastMM证实了这一点。 在一个特定客户机的服务器上,我会随机崩溃。服务器规格与我们其他客户机的规格非常一致(我们实际上已经在各种硬件上进行了尝试),程序使用的文件也是如此(据我所知,有一些超敏感的材料我无法访问,但那里似乎没有什么不寻常的东西) 我试过像尤里卡洛格和马德希这样的人,也许是为了缩小问题的范围,但不幸的是,他们似乎只是偶尔在崩溃时抓住了一个例外,而不是所有时候。当它这样做时,通常会在崩溃之前显示一个或多个“内存不足”错

我有一个没有内存泄漏的GUI应用程序。我已经在多次测试循环中用FastMM证实了这一点。 在一个特定客户机的服务器上,我会随机崩溃。服务器规格与我们其他客户机的规格非常一致(我们实际上已经在各种硬件上进行了尝试),程序使用的文件也是如此(据我所知,有一些超敏感的材料我无法访问,但那里似乎没有什么不寻常的东西)

我试过像尤里卡洛格和马德希这样的人,也许是为了缩小问题的范围,但不幸的是,他们似乎只是偶尔在崩溃时抓住了一个例外,而不是所有时候。当它这样做时,通常会在崩溃之前显示一个或多个“内存不足”错误

所以我想可能有些对象释放得太晚了,也就是说,只有在应用程序关闭的时候,而不是我想释放它们的时候?我看过FastMMUsageTracker的演示,但不能完全理解。有文件吗?或者有人能用(有些容易理解的)话来说明我如何去检查这个吗

或者,检测应用程序正在接近其“内存限制”的最佳方法是什么,以便采取一些预防措施?如果我理解正确,一个普通的Delphi应用程序是32位的,它可以很好地处理2Gb的内存(当然前提是硬件支持),对吗

注:德尔福2009或XE,如果相关的话

谢谢

编辑-问题可能已解决

我们发现了一个问题,一个弹出窗口在一段时间后自动关闭和释放,其创建速度比消失速度快得多。随着时间的推移,这会消耗大量内存,然后任何内存分配基本上都会将其带到边缘,并触发“内存不足”问题

这可以解释为什么堆栈跟踪不一致的地方

我不完全相信这是我们唯一的问题,因为,即使不太可能,在我们的应用程序运行的这些年中,这种情况也可能发生过,但不知何故它没有发生。我会在这个问题上做更多的挖掘


感谢所有回答的人,每个答案实际上都包含有有价值的信息。

如果您有Delphi XE,它附带了AQTime,AQTime有一个内存分配分析器作为其技巧包的一部分。如果在程序上运行,您可能会看到RAM的去向。

您可以了解应用程序正在使用的内存量-请参阅本页。总结:

uses PsAPI;

//current memory size of the current process in bytes
function CurrentMemoryUsage: Cardinal;
var
  pmc: TProcessMemoryCounters;
begin
  pmc.cb := SizeOf(pmc) ;
  if GetProcessMemoryInfo(GetCurrentProcess, @pmc, SizeOf(pmc)) then
    Result := pmc.WorkingSetSize
  else
    RaiseLastOSError;
end;
ShowMessage(FormatFloat('Memory used: ,.# K', CurrentMemoryUsage / 1024)) ;
如果您定期在服务器中记录该值,您至少可以了解发生了什么。结果中有更多的信息可以帮助您了解更多关于您的程序正在做什么的信息

解决方法是查看实际使用内存的内容,并更积极地管理它。我怀疑在某个地方,您正在创建对象,并且只有在关机时才能释放它们,而您可以(而且应该)在完成这些对象后立即释放它们

一个可能的修复方法是在完整版本的FastMM上使用/3GB开关,看看问题是否需要更长的时间才能出现

如果您非常不走运,您将“破坏”FastMM的内存池管理算法,因此它永远不会释放内存()。尝试不同的内存管理器可能会对您有所帮助,因为有些管理器在回收未使用的内存方面更具攻击性。但是,如果你正在分割你的堆,唯一真正的解决办法就是找出如何避免这样做。这是一个复杂的主题,所以请再说一遍:首先尝试上面简单的事情。

当我出现“内存不足”错误时,它是由于失控循环造成的。循环通常会分配内存,并且在使用所有可用内存之前不会停止。释放内存不是一个问题,因为程序永远不会到达那个点。被咬过的代码类型有:

“while not x.Eof do”循环,没有“x.Next”在数据集中前进,或者

从未遇到退出条件的递归过程或子例程

我会寻找任何类型的循环或递归,在某些情况下可以“永远”继续,例如在内存中构建一个庞大的数据结构。

忘记“Windows”内存-您想要的是应用程序分配的实际内存。这是判断您是否正在分配未随时间释放的内存的唯一方法。对于使用FastMM的Delphi 2006+,您需要:

//------------------------------------------------------------------------------  
// CsiGetApplicationMemory  
//  
// Returns the amount of memory used by the application (does not include  
// reserved memory)  
//------------------------------------------------------------------------------  
function CsiGetApplicationMemory: Int64;  
var  
  lMemoryState: TMemoryManagerState;  
  lIndex: Integer;  
begin  
  Result := 0;  

  // get the state  
  GetMemoryManagerState(lMemoryState);  

  with lMemoryState do begin  
    // small blocks  
    for lIndex := Low(SmallBlockTypeStates) to High(SmallBlockTypeStates) do  
      Inc(Result,  
          SmallBlockTypeStates[lIndex].AllocatedBlockCount *  
          SmallBlockTypeStates[lIndex].UseableBlockSize);  

    // medium blocks  
    Inc(Result, TotalAllocatedMediumBlockSize);  

    // large blocks  
    Inc(Result, TotalAllocatedLargeBlockSize);  
  end;  
end;  

我以10秒到10分钟的间隔将此记录到日志文件中,以及与上次的差异。

当您收到错误时,能否向我们显示堆栈跟踪?发生错误时,它消耗了多少内存

不久前,我制作了一个内存记录器(用于FastMM),记录两点之间的所有内存(使用“startlog”和“endlog”过程),以查找“软泄漏”:我在列表中分配对象,但从未清除列表,只有在关闭应用程序时,FastMM才报告没有泄漏。通过使用内存记录器,我可以找到这些对象(它只记录在执行“endlog”过程之前未释放的新分配内存)
我会看看是否能找到这个代码

顺便说一句:你可以通过其他三种方式获得“内存不足”的结果:

  • FastMM在分配时检测到错误时会给出此错误。因此不是真正的“内存不足”,而是内部fastmm错误(由于损坏等)
  • 分配一个非常大的块(例如1Gb)。有一次,由于流读取错误(Rejects),所以它读取了一个错误的字符串大小值,所以它尝试预分配一个(随机)大字符串。这样的错误看起来很奇怪,因为在我的例子中,我的应用程序分配了大约150Mb的内存,所以也没有真正的“内存不足”
  • 碎片:如果您试图分配一个10Mb的块,但Windows找不到一个10Mb的连续块
    const
      IMAGE_FILE_LARGE_ADDRESS_AWARE = $0020;
    
    {$SetPEFlags IMAGE_FILE_LARGE_ADDRESS_AWARE}