Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Delphi memory leak with pointer to TStringList

I'm having a little trouble understanding the behaviour of pointers. I have a very simple example to demonstrate:

type
  PSL = ^TStringList;
...
var
  myPSL : PSL;
...
procedure TForm1.FormCreate(Sender: TObject);
begin
  New(myPSL);
  myPSL^ := TStringList.Create;
  myPSL^.Add('String 1');
  myPSL^.Add('String 2');
  myPSL^.Add('String 3');
end;
...
procedure TForm1.FormDestroy(Sender: TObject);
begin
  Dispose(myPSL);
end;

With this code I get this memory leak report

29 - 36 bytes: UnicodeString x 3
37 - 44 bytes: Unknown x 1
85 - 92 bytes: TStringList x 1

Now, if I call

myPSL^.Free;

before disposing of the pointer, then nothing is reported.

I can't understand why this is happening. I know that calling New() allocates enough memory (based on the type of the pointer) and calling Dispose() takes care for deallocating that same memory, then why do I need Free the pointer as if it was a "real" object?

Thanks!

like image 962
tsmr Avatar asked Dec 02 '22 14:12

tsmr


2 Answers

Class objects are already referenced by pointer. It makes very little sense to define a pointer to a reference type. Just use the type directly:

var
  myPSL : TStringList;
...
procedure TForm1.FormCreate(Sender: TObject);
begin
  myPSL := TStringList.Create;
  myPSL.Add('String 1');
  myPSL.Add('String 2');
  myPSL.Add('String 3');
end;
...
procedure TForm1.FormDestroy(Sender: TObject);
begin
  myPSL.Free;
end;

What your code actually does is following:

New(myPSL) and Dispose(myPSL) are simply allocating/deallocating a memory block that is sizeof(TStringList) bytes in size, but are not actually constructing/destructing the TStringList object within that memory. You need to call myPSL := TStringList.Create and myPSL.Free for that instead.

like image 127
Dalija Prasnikar Avatar answered Dec 05 '22 02:12

Dalija Prasnikar


You do not need to store pointer to TStringList, instance of TStringList is actually also pointer. To solve it, change type of myPSL to something like below:

var
   myPSL : TStringList;
...
procedure TForm1.FormCreate(Sender: TObject);
begin
  myPSL := TStringList.Create;
  myPSL.Add('String 1');
  myPSL.Add('String 2');
  myPSL.Add('String 3');
end;
...
procedure TForm1.FormDestroy(Sender: TObject);
begin
  myPSL.Free;
end;

If somehow, you still need to use your old code, to avoid memory leak, you need to call Free before you dispose myPSL:

myPSL^.Free;
dispose(myPSL);
like image 24
Zamrony P. Juhara Avatar answered Dec 05 '22 04:12

Zamrony P. Juhara