FastMM 报告 FormDestroy 上释放的类的内存泄漏

Posted

技术标签:

【中文标题】FastMM 报告 FormDestroy 上释放的类的内存泄漏【英文标题】:FastMM report memory leak on a class freed on FormDestroy 【发布时间】:2013-03-31 15:32:03 【问题描述】:

我遇到了一个 Delphi 7 应用程序(CLX)的内存泄漏问题,代码如下:

unit Unit2;

interface

  uses ECRClass, ECR_Text,  SysUtils, Types, Classes, Variants, Math;

  type tLeakClass = class
  private
  fsType : integer;

  public
  fsPrinter : TECR_Class;

  published
  constructor Create (AOwner : TComponent);
  destructor Destroy();
  end;


implementation

   constructor tLeakClass.Create (AOwner : TComponent);
   begin
   fsPrinter := TECR_Text.Create(AOwner);
   end;

   destructor tLeakClass.Destroy();
   begin
     fsPrinter.Free
   end;

end.

对象fsPrinter即使在主窗体(TForm)关闭时被释放,结果也会泄露:

unit Unit1;

interface

uses
  SysUtils, Types, Classes, Variants, QTypes, QGraphics, QControls, QForms, 
  QDialogs, QStdCtrls, Unit2;

type
  TForm1 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
  private
     Private declarations 
  public
     Public declarations 
    tleak : tLeakClass;
  end;

var
  Form1: TForm1;

implementation

$R *.xfm

procedure TForm1.Button1Click(Sender: TObject);
begin
     tLeak := tLeakClass.Create(Self);
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
   tleak.Free
end;

end.

这是 FastMM4 的泄漏报告:

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

This block was allocated by thread 0xBA8, and the stack trace (return addresses) at the time was:
402F1C [system.pas][System][@GetMem][2439]
403C77 [system.pas][System][TObject.NewInstance][8360]
404012 [system.pas][System][@ClassCreate][9019]
502F15 [ECR_Text.pas][ECR_Text][TECR_Text.Create][101]
403C80 [system.pas][System][TObject.NewInstance][8360]
404012 [system.pas][System][@ClassCreate][9019]
5030C6 [Unit2.pas][Unit2][tLeakClass.Create][24]
43856C [QStdCtrls.pas][QStdCtrls][2863]
503300 [Unit1.pas][Unit1][TForm1.Button1Click][30]
447076 [QControls.pas][QControls][TControl.Click][1977]
43858C [QStdCtrls.pas][QStdCtrls][TButton.Click][2871]

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

Here 您可以下载代表问题的项目的完整SSCCE 示例(单击按钮运行示例并关闭表单)。

为什么fsPrinter 对象会泄漏?我怎样才能避免泄漏?

【问题讨论】:

如果您多次单击该按钮,您将覆盖TLeak 字段中对TLeakClass 对象的引用,从而将这些对象变为孤立对象。您只需要保留对所有这些对象的引用,或者在创建新对象之前调用TLeak.Free 不,我只点击了一次。这只是说明问题的一个例子。您必须单击一次按钮,然后关闭表单... 好吧,那么使用OnCreate 表单事件可能会更好。 这是一个更好的例子。做得好。对于构建非常小的 SSCCE,我的建议是制作控制台应用程序。 【参考方案1】:

你的析构函数声明不正确。你写道:

destructor Destroy();

但是您必须覆盖TObject 中声明的虚拟析构函数。如果你不这样做,那么Free 将不会调用你的析构函数,它会调用TObject 中声明的虚拟析构函数。

像这样修复它:

destructor Destroy(); override;

尽管在这种情况下并不重要,但您应该养成在构造函数和析构函数中调用继承的构造函数和析构函数的习惯。这样,当您派生的类在其构造函数和析构函数中的作用超过 TObject 时,您将确保超类代码运行。

constructor tLeakClass.Create (AOwner : TComponent);
begin
  inherited Create;
  fsPrinter := TECR_Text.Create(AOwner);
end;

destructor tLeakClass.Destroy();
begin
  fsPrinter.Free;
  inherited;
end;

FastMM 报告有点奇怪。它报告TECR_Text 对象已泄露。但是由于您创建了该表单所拥有的内容,因此表单应该将其删除。问题代码中明显泄露的对象是tLeakClass的实例

所以我怀疑课程中还有其他我们看不到的问题。很可能您犯了同样的错误,并在我们看不到的类的析构函数上省略了override

【讨论】:

不在我的代码版本中。当然,我没有和你一样的课。这是一个更好的例子,但我无法编译它。我必须用别的东西代替TECR_Text。您确实需要提供一个我可以按原样编译的示例。让它成为一个控制台应用程序,并且只使用标准类。 哦,是的,对不起。现在我明白了!抱歉,我对 Delphi 很陌生 :-) 顺便说一句,学习如何制作真正优秀的 SSCCE 不仅可以帮助您提出好问题。主要好处是您将能够查看包含缺陷的非常少量的代码。这极大地增加了您发现问题的机会。在我看来,这是编写好的 SSCCE 的最大好处。

以上是关于FastMM 报告 FormDestroy 上释放的类的内存泄漏的主要内容,如果未能解决你的问题,请参考以下文章

FastMM 报告 C++ Builder 6 中 STL 容器的内存泄漏

delphi fastmm4 调试

Delphi7 中使用FastMM 转载

Delphi - 检查内存是不是“按时”释放

这是怎么内存泄漏的?

fastmm4中文翻译