1

我们正试图弄清楚我们的软件中是否存在内存泄漏。所以,我一直在使用各种工具和程序来帮助我找到可能的内存泄漏。我使用的软件之一是 AQTime。由于它与 Delphi XE 一起提供,它只是一个演示。所以,我真的无法从中获得任何有用的信息。然后,我决定使用免费软件 MemProof。到目前为止,它向我展示了我们软件的许多需要注意的问题。其中之一是错误。

一旦我通过 MemProof 启动我的程序,它就会列出 2 个错误,它们试图从单元文件 system.pas 中销毁不存在的对象。因此,当我实际上在 TObject.Free 过程中放置​​一个断点时,它甚至在我的程序一直启动之前就中断了。通过 system.pas 中的 Free 过程,我发现 TIconimage 正在尝试破坏或释放自身。换句话说,在实际启动之前,不会从我的程序中调用免费过程。

这是实际的免费程序:

procedure TObject.Free;
begin
  if Self <> nil then
    Destroy;
end;

在那次观察之后,我删除了断点,让程序一直运行。我的程序主窗口弹出准备用户输入。但是,我还发现如果我的程序窗口的任何部分显示在屏幕上,就会不停地调用 TObject.Free 过程。我完全不明白。这是为什么?谁能解释一下?当 TForm 显示在屏幕上时,程序不断被调用,TForm 与任何形状或形式的 TObject.Free 有什么关系?

提前致谢。

4

3 回答 3

11

关于为什么TObject.Free执行很多,每次销毁一个对象时,任何对象都会调用该方法。所有的类都派生自 TObject,它是共同的祖先,所以 Delphi 程序中的几乎任何动作都涉及大量的对象创建/销毁对,因此会命中TObject.Free.

关于内存泄漏的检测,您需要在 Delphi 中内置来解决这个问题。FastMM 内存管理器可以在“报告内存泄漏”模式下运行,它会为您提供任何内存泄漏的诊断负载。

考虑以下简单的程序:

program Leaker;
begin
  ReportMemoryLeaksOnShutdown := True;
  TObject.Create;
end.

这将产生以下输出:

在此处输入图像描述

你只需要ReportMemoryLeaksOnShutdown在你的应用程序的某个地方设置为 True (.dpr 文件的开头和任何地方一样好)。

如果您希望在报告中获得更多信息,那么您可以下载完整版 FastMM并将其配置为您心中的内容。

然后你会得到这样的输出:

A memory block has been leaked. The size is: 84

This block was allocated by thread 0x1304, and the stack trace (return addresses) at the time was:
40455E [System][System.@GetMem]
405A2F [System][System.TObject.NewInstance]
40602E [System][System.@ClassCreate]
4474C2 [Classes][Classes.TStringList.Create]
C275A3 [Main.pas][Main][Main.TMainForm.CreateAuxiliaryForms][997]
C84C8A [OrcaFlex.dpr][OrcaFlex][OrcaFlex.OrcaFlex][351]
75E633CA [BaseThreadInitThunk]
77519ED2 [Unknown function at RtlInitializeExceptionChain]
77519EA5 [Unknown function at RtlInitializeExceptionChain]

The block is currently used for an object of class: TStringList

这真是太棒了。它告诉我泄漏的内存是在 Main.pas 第 997 行分配的,而这正是我故意泄漏的地方!

于 2011-03-16T21:28:36.727 回答
2

如您所知,您可以在项目选项的应用程序设置中分配TApplication一个属性。Icon此属性反映FIcon在对象的构造函数中创建的 TApplication 字段中ApplicationTIcon有一个TIconImage表示在其构造函数中创建的实际图像的字段。当应用程序对象加载并分配项目资源文件中的图标时,必须释放此初始“TIconImage”以防止泄漏。这一切都发生在项目源代码中调用之前Application.Initialize,因为 Application 对象是从“controls.pas”的初始化部分构造的。

当应用程序启动或运行时,会发生很多事情。启动时,流式传输机制创建对象(资源流、读取器、类查找器、组件列表......),然后释放它们。即使是一个空白的 VCL 表单(上面没有控件),在运行时,每次它被激活时都会创建一个列表以找到一个控件来放置焦点,然后释放这个列表。对于复杂的 GUI 应用程序,即使您将鼠标悬停在某物上,也可以创建和释放各种图形对象。或者,即使您将鼠标按在某物上,对齐/排列代码也可以创建/释放对象。

要调试泄漏,您可以参加David 的回答概述的课程,或者在使用 3rd 方产品时专注于它所说的泄漏,而不是每个被创建/释放的对象。:)

于 2011-03-16T22:25:27.513 回答
0

只要 Delphi 中的类的任何实例是Free,就会调用TObject.Free

这包括作为 Delphi 程序正常执行的一部分而创建和销毁的大量对象,包括响应由TForm对象自动处理的事件,以响应系统生成的消息,仅用于维护窗口对象本身存在于Windows自己的窗口管理器中。

例如,考虑TCustomForm WndProc中的这段代码片段:

      WM_MEASUREITEM:
      begin
         :
                Canvas := TControlCanvas.Create;
                with Canvas do
                try
                  :
                finally
                  Canvas.Free;
                end;
        :
      end;

这里的关键是响应WM_MEASUREITEM消息,自定义表单(因此是标准的TForm派生类,因为它最终派生自TCustomForm)创建一个临时的TControlCanvas,然后当它完成时它是Free的。

这可能不一定是您在特定表单的情况下看到的TObject.Free调用的来源,这只是一个示例,但显示了仅存在的TForm如何导致其他对象的存在和销毁以响应自动,系统生成的消息。

于 2011-03-17T02:54:55.300 回答