FastMM report memory leak on a class freed on FormDestroy

431 views Asked by At

I have encountered a memory leak problem with a Delphi 7 application(CLX), with a piece of code like the following :

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.

the object fsPrinter result leaked even if it is freed at the moment that the main form(TForm) close :

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.

Here's the report of the leak by 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 you can download a full SSCCE example of a project that represent the problem(to run the example one click on the button and close the form).

Why the fsPrinter object leaks ? How can I avoid the leak ?

1

There are 1 answers

3
David Heffernan On BEST ANSWER

You destructor is declared incorrectly. You wrote:

destructor Destroy();

But you must override the virtual destructor declared in TObject. If you don't then your destructor will not be called by Free which calls the virtual destructor declared in TObject.

Fix it like this:

destructor Destroy(); override;

Although it doesn't matter in this case, you should get in to the habit of calling the inherited constructors and destructors in your constructors and destructors. That way, when you derive from a class that does more than TObject does in its constructors and destructors, you will make sure the superclass code runs.

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

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

The FastMM report is a bit odd though. It reports that the TECR_Text object is leaked. But since you created that as owned by the form, the form should take it down. The object that is clearly leaked in the code in the question is the instance of tLeakClass

So I suspect that there are other problems in the classes that we cannot see. Quite possibly you made the same error and omitted the override on the destructors of the classes that we cannot see.