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

3

我遇到了一个内存泄漏问题,这个问题出现在使用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.

即使在主窗体(TForm)关闭时释放了对象fsPrinter,它的结果仍然会泄漏:

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

这里可以下载一个包含问题的完整SSCCE项目示例(单击按钮并关闭窗体即可运行示例)。

为什么fsPrinter对象会泄漏?我如何避免这种泄漏?


如果您多次单击该按钮,您将覆盖 TLeak 字段中对 TLeakClass 对象的引用,从而将这些对象变为孤立状态。您只需保留对所有这些对象的引用,或在创建新对象之前调用 TLeak.Free 即可。 - TLama
不,我只点击了一次。这只是一个示例,用于演示问题。您必须单击一次按钮,然后关闭表单... - aleroot
那么,最好使用窗体的 OnCreate 事件来实现这个功能。 - TLama
这是一个更好的例子。干得好。我建议构建真正小的SSCCE是制作控制台应用程序。 - David Heffernan
1个回答

6
您的析构函数声明有误。您写成了:
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。你真的需要提供一个我可以直接编译的例子。让它成为一个控制台应用程序,并且只使用标准类。 - David Heffernan
哦,是的,抱歉。现在我明白了!抱歉,但我对Delphi还很陌生 :-) - aleroot
顺便提一下,学习如何编写一个真正好的SSCCE不仅有助于你提出好问题。主要的好处是,你将能够查看包含缺陷的非常少量代码。这极大地增加了你发现问题的机会。在我看来,编写良好的SSCCE的最大好处就是这个。 - David Heffernan

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