Delphi - 检查内存是否“按时”发布

时间:2011-03-10 01:13:53

标签: delphi memory out-of-memory

我有一个没有内存泄漏的GUI应用程序。我已经通过FastMM在多个测试周期中证实了这一点。 在一个特定客户端的服务器上,我得到随机崩溃。服务器规格与我们其他客户端的规格完全一致(我们实际上已尝试过各种硬件),程序使用的文件也是如此(据我所知,我有一些超敏感材料)无法访问,但似乎没有任何与众不同的东西)。

我曾试过像EurekaLog和MadShi这样的人,可能会缩小这个问题的范围,但不幸的是,他们似乎只是偶然发现了一个例外,而不是所有时间。当它发生时,它通常会在崩溃之前显示一个或多个“Out of memory”错误。

所以我想也许某些对象可能“太迟”了,即只有当应用程序关闭时才会被释放,而不是当我意味着释放它们时?我见过FastMMUsageeTracker演示,但实际上并没有真正理解它。有文件吗?或者有人可以输入(有些可访问的)单词来检查这个问题吗?

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

PS:Delphi 2009或XE,如果那是相关的

谢谢!

编辑 - 问题可能已解决

我们能够找到一个问题,一个弹出窗口关闭并在一段时间后自动释放自己的速度比它消失的速度快得多。随着时间的推移,这将占用大量内存,然后任何内存分配基本上会使其超越边缘并触发“内存不足”问题。

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

我并不完全相信这是我们唯一的问题,因为尽管不太可能,但在我们的应用程序运行多年之前,这种情况很可能已经发生,但不知何故它还没有。我会在这个问题上做更多的挖掘。

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

6 个答案:

答案 0 :(得分:7)

如果你有Delphi XE,它附带AQTime,而AQTime有一个内存分配探查器作为其技巧的一部分。如果你在程序上运行它,你可能会看到你的RAM在哪里。

答案 1 :(得分:6)

忘掉“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分钟之间)记录到我的日志文件中,以及与上次的差异。

答案 2 :(得分:3)

您可以了解应用程序使用的内存量 - 请参阅此About页面。总结:

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的内存池管理算法,因此它永远不会释放内存(a related question)。尝试使用不同的内存管理器可能会对您有所帮助,因为有些人会更积极地回收未使用的内存。但是,如果你正在分割你的堆,唯一真正的解决方案是弄清楚如何避免这样做。这是一个复杂的话题,所以再次:首先尝试上面的简单内容。

答案 3 :(得分:2)

收到错误后,您能告诉我们堆栈跟踪吗?错误发生时消耗了多少内存?

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

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

  • FastMM在分配时检测到错误时会出现此错误。所以不是真正的“内存不足”,而是更多的内部fastmm错误(由于腐败等)
  • 分配一个非常大的块(例如1Gb)。我曾经因为流读取错误(RemObjects)而得到这个,所以它为字符串读取了错误的大小值,所以它试图预先分配一个(随机)大字符串。这样的错误看起来很奇怪,因为在我的情况下我的应用程序分配了大约150Mb所以也没有真正的“内存不足”
  • 碎片:如果你尝试分配一个10Mb的块,但Windows找不到一个10Mb的连续块,那么Windows会给出“内存不足”。

因此,请提供错误时使用的堆栈跟踪和内存量!

答案 4 :(得分:2)

由于Delphi对32位地址空间的限制,这些问题变得越来越普遍。

您可以做的第一件也是最简单的事情是在64位操作系统上运行,并从2GB可用地址空间(当您使用32位操作系统时)移动到4GB地址。这不会自动发生。您需要将您的应用程序标记为LARGEADDRESSAWARE。通过将以下内容添加到.dpr文件来执行此操作:

const
  IMAGE_FILE_LARGE_ADDRESS_AWARE = $0020;

{$SetPEFlags IMAGE_FILE_LARGE_ADDRESS_AWARE}

内存不足错误的另一个常见原因并不是存在大量内存,而是要求大量连续内存,并且没有单个连续的可用地址空间块。

处理这个问题更加困难。首先需要确定当前需要大量连续内存块的代码部分。接下来,您必须修改正在这样做的对象,并安排他们改为询问小块内存,然后您可以“拼接”以呈现更大块的外观。根据我的经验,这通常发生在使用动态数组的代码中。

答案 5 :(得分:1)

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

“没有x.Eof do”循环,没有“x.Next”来推进数据集,或

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

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