我的程序从不释放内存,为什么?

3
我有一个MDI程序。启动时需要2-3MB的RAM。然后,在程序中我创建了大约260个MDI子窗口(每个窗口都有一个TStringGrid、一个位图和一些其他控件)并显示一些数据。加载所有这些窗口需要大约500MB。如果我手动关闭每个MDI子窗口,应用程序仍然使用160MB的RAM。为什么它不返回到几MB的RAM?我应该担心吗?160MB对于只有1GB RAM的系统来说太多了!注意:我使用任务管理器中的“工作集”列来查看RAM统计信息。也许我需要一个更好的工具来读取RAM利用率。(私有工作集比工作集略小。)这不是泄漏!当我关闭程序时,FastMM(设置为侵略性)没有显示出内存泄漏。请参阅我的答案帖子以获取额外证据证明这不是泄漏。我释放东西。许多人告诉我关闭子窗口只是隐藏它。我知道。我使用“Action:= caFree”来实际释放表单。每个表单负责释放它所持有的控件。

答案
我发现这是由FastMM引起的。请参见我下面发布的答案。


Delphi 7,Win 7 32位
类似帖子:
可以清理内存吗?
何时调用SetProcessWorkingSetSize?(说服内存管理器释放内存)


这个问题无法回答。 - Andreas Rejbrand
1
你怎么知道你的应用程序仍在使用160MB的RAM? - jachguate
@Altair 作为一个简单的测试,你可以创建一个空的应用程序。添加一个按钮,其OnClick事件通过调用GetMem来分配与你的真实应用程序相同类型的内存。确保在分配内存的同一事件处理程序中对内存进行解除分配。与你的真实应用程序相比,它的表现如何? - David Heffernan
1
a) 工作集 <> 堆分配 b) 任务管理器显示瞬时值,而非统计数据 - Free Consulting
显示剩余10条评论
5个回答

7

任务管理器不是检测内存泄漏的正确工具。Delphi分配大块内存并将其保留以备将来使用,因此即使在释放所有资源后,分配的内存量也会有所增加。只有使用专业的内存分析工具才能获得其他结果(以及更详细的答案)。 AQTime是首选的工具,或者如果您可以找到旧但有用的MemProof,它会对您有很大帮助(MemProof是免费的,并且对于内存分析比AQTime更方便)。


即使您释放了所有资源,也预计会有一定的分配内存增加。我知道这一点。但是我没有预料到会有如此大量的内存增加。我将尝试使用MemProof。编辑:实际上,如果它为每个最近关闭的窗口保留约1MB的RAM,则这是有道理的。 - Gabriel
如果你最小化了你的程序,你的应用程序内存使用量突然下降了吗?那么你的结论基于错误的统计数据。 - Lars Truijens
@Lars-我熟悉这种情况(似乎自Win XP以来就存在)。这是要尝试的第一件事 :) 不幸的是,这个应用程序没有出现这种情况。 - Gabriel

4
很有可能FastMM在应用程序终止时不显示内存泄漏(例如,因为所有对象都是TComponents,而且拥有者释放了它们)。但同时,当运行这些组件时,它们仍可能存在,并且不能很快释放。
您是否使用显示当前内存使用情况的FastMM单元? 这是目录...\FastMM\Demos\Usage Tracker中的FastMMUsageTracker.pas。 使用该单元,然后调用其中的ShowFastMMUsageTracker函数。 您可以定期刷新该窗体以查看内存消耗的增长情况。 我已经放置了一个FastMMUsageTrackerProject示例on-line,其中包括更新的FastMM4,使检查和调试内存泄漏更加容易。
  • FastMMUsageTracker单元中的表单现在可调整大小,并且其中的控件以正确的方式锚定
  • 有一个新的FastMmBootstrapUnit单元,使得调试特定的内存泄漏变得更加容易

上周我手头拿着一个第三方DLL,它不是用Delphi编写的。
该DLL使用Windows GlobalAlloc调用存在内存泄漏,而这些内存泄漏无法被FastMM跟踪。

NB: 我即将发布FastMM的更新版本

--jeroen


有一个FastMM单元可以显示当前内存使用情况的窗体吗?我不知道这个单元。也许它只适用于Delphi 2010?你能给我一些名字的提示,这样我就可以在谷歌上搜索了吗? - Gabriel
人们说FastMM不会失败:https://dev59.com/BlLTa4cB1Zd3GeqPdMPX,我认为我同意他们。 - Gabriel
@Jeroen-谢谢。我会立即使用那个单元。我已经检查了我的代码和第三方库,没有使用GlobalAlloc。 - Gabriel

4

答案:

我刚刚从我的项目中移除了FastMM,释放所有那些子窗口之后,程序的内存占用量降低到了几兆字节。很多人可能会认为这不是一个错误行为,而是FastMM在做某种奇特的内存优化。他们可能是正确的。然而,这对于我的应用程序可能是好的,但对于其他正在运行的应用程序可能不是好的。

所以,至少我们知道是谁引起了这个问题。我整整一天都担心我的程序像一个旧桶一样泄漏内存。现在我松了一口气。

更新:

为了确认这种行为是否由FastMM生成(正如Barry Kelly所建议的那样),我创建了第二个程序来分配大量的RAM。当Windows耗尽内存时,我的程序的内存利用率恢复到了原来的值。
(注意:我并没有说FastMM存在缺陷!)

我的程序没有泄漏。问题解决了。


0
FastMM的内存泄漏跟踪的主要限制是只能在关闭程序时运行。这可能是因为您仍然持有对对象或其他数据的引用,这些数据在关闭程序时会被清除,但在此之前会一直存在。
例如,当您关闭MDI子窗口时,您是否调用了Free或Release函数?还是只是让它们消失了?如果它们被隐藏但没有被释放,它们仍然会占用内存。

我释放了这些窗体。如果子窗体只是被隐藏,那么内存的使用量不会从500MB降至160MB。 - Gabriel
你确定表单只是保存着它们所显示数据的引用对象吗?可能并不是表单没有释放内存。 - Kenneth Cochran
@codeelegance. 是的。如果我没有释放一些对象(泄漏内存),FastMM将立即跳出来提醒我。 - Gabriel
2
正如Mason所指出的那样,FastMM只会在程序终止时发出警告。您询问了程序在运行时的内存使用情况。只要对象在终止之前已被释放,FastMM就不会出现问题。 - Kenneth Cochran
只要对象在终止之前已被释放,FastMM就不会出现问题——我同意你的观点!!这意味着(由于我的FastMM没有抱怨),我没有泄漏内存!这很好。问题是:FastMM不释放内存对其他程序是否有益?也许其他程序非常需要那些内存。 - Gabriel

0
如果您关闭一个 MDI 窗体,它不会自动释放。使用 Action = caFree(请搜索此内容)确保该窗体也被释放。

我释放了表单(请参见以前的答案)。 - Gabriel

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接