Thursday, November 22, 2007

Some memory leak pitfalls

Just installed the professional version of Eurekalog, which is, imo, a must have tool for tracking exceptions and memory leaks in your Delphi Win32 applications.
This tool, and of course any other tool out there that does the same, really helps you to track down exceptions fast. For me, even more important, is the fact that it helps you track down memory leaks as well.

No matter how secure you program, sooner or later, you will loose some memory. Here some pitfalls:

1. Let it be clear where, and who, will free the object
It must be clear where objects are created (read memory for the object is allocated) because otherwise you are in danger for memory leaks.
Functions that create their result are dangerous in this manner. Suppose a function that creates a TStringList, and returns it as it's result:

function TForm3.GetSomeStringList : TStrings;
begin
Result :
= TStringList.Create;
Result.Add(
'one');
Result.Add(
'two');
end;

procedure TForm3.DoSomethingWithaStringlist;
var
MyStringList : TStrings;
begin
MyStringList :
= GetSomeStringList;
try
ShowMessage(MyStringList.Text);
finally
MyStringList.Free;
//Free it!
end;
end;

In this case, the calling method, and the developer must realize that it must free the stringlist, after it is ready with it. This is not always clear in this scenario. A better solution would be to pass your own created Stringlist to the function. Like this:
procedure TForm3.GetSomeStringList(
AStringList : TStrings);
begin
AStringList.Add(
'one');
AStringList.Add(
'two');
end;


procedure TForm3.DoSomethingWithaStringlist;
var
MyStringList : TStrings;
begin
MyStringList :
= TStringList.Create;
try
GetSomeStringList(MyStringList);
ShowMessage(MyStringList.Text);
finally
MyStringList.Free;
//Free it!
end;
end;


It is better to have both, the Create and Free in the same try finally block if possible. Can't go wrong if you code it like this.


2. Always free your own objects nobody else does it for you 
Sometimes it is easy to add to, for example a listbox, also an extra object with more information of the selected item. When filling the items (a TStringlist) you pass on a reference to the listbox item. Later you can use that information to do, whatever you want to do.


Your code could look like this:

procedure TForm3.Button1Click(Sender: TObject);
var
obj : TSomeObject;
begin
obj :
= TSomeObject.Create;
obj.SomeInfo :
= 'This is number one';
ListBox1.Items.AddObject(
'one', obj);
obj :
= TSomeObject.Create;
obj.SomeInfo :
= 'This is number two';
ListBox1.Items.AddObject(
'Two', obj);
end;

To read the data on the selection you can then read the extra info like this (just an example here):
ShowMessage(
(ListBox1.Items.Objects[ListBox1.ItemIndex]
as TSomeObject).SomeInfo);

What you must not forget in this situation is to free all the objects when the application or form is destroyed! The Listbox does not do that for you!

So in the destroy of your form, free all the objects in the listbox like this:
for i := 0 to ListBox1.Items.Count - 1 do begin
Listbox1.Items.Objects[i].Free;
end;

Conclusion is that a good coding style helps you to avoid memory leaks. Tools like Eurekalog helps you to trace them easily and are a great learning resource for where you enter memory pitfalls

No comments: