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 ?
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.