Memory Leak in Delphi and How to avoid it
What is Memory Leak
Following an example of alert caused by TStringList.Create;
Every time
when we no longer use an object in our code, we should delete it, by freeing
the memory it was allocated. If we don't do this, our program can allocate more
and more memory as it runs. This will also fail to remove unwanted blocks of
memory is called a memory leak. And if we run the program long enough without
solving memory leak issue then our program will use up the memory resources of
our PC and the PC will slow down and eventually hang at last.
What are the problems faced by memory
leaks?
- Memory leak
slows down the system and doesn’t let the normal applications to run too.
- Memory leak
causes more problems for the overall speed and generate lots of error message
like Stack overflow, etc.
- Memory leak
also doesn’t allow other applications on the system to run due to the memory
overflow error.
- It can be
detected and application can be made such that while its termination it
releases the memory as well.
- It doesn’t
run for a very long time and a restart of the system might help in recovering
the losses.
How the memory leaks
normally occur
The first
step to avoiding memory leaks is to understand how they occur. Follow is a
discussion on some common pitfalls and best practices for writing non-leaking
Delphi code. In many parts of many
programs, you will have a function or procedure that creates one or more
objects in order to carry out it's operation. Here is an example :
procedure SaveStudentList;
var
slStudent : TStringList;
begin
// Create the TSTringList object
slStudent:= TStringList.Create; // This allocates object memory
// Load the students list into the string list
slStudent.Add(‘Jack’); // Expands the object memory size
slStudent.Add(‘Shawn’); // Expands the object memory size
slStudent.Add(‘Steve’); // Expands the object memory size
slStudent : TStringList;
begin
// Create the TSTringList object
slStudent:= TStringList.Create; // This allocates object memory
// Load the students list into the string list
slStudent.Add(‘Jack’); // Expands the object memory size
slStudent.Add(‘Shawn’); // Expands the object memory size
slStudent.Add(‘Steve’); // Expands the object memory size
slStudent.Add(‘Pete’); //
Expands the object memory size
//
Now prepare to save to file
slStudent.SavetoFile(‘C:\StudentList.txt’);
end;
slStudent.SavetoFile(‘C:\StudentList.txt’);
end;
Each time this
procedure is called, a new TStringList object is created and filled with the
list of Students You might think that Delphi would discard the old object when
you are assigning the new object to slStudent. But this does not happen because
Delphi variable only referring to the object.
So you must add the
following code to the end of the procedure to free up the
memory:
// Free
up the string list memory
FreeandNil(slStudent);
FreeandNil(slStudent);
This allows the
procedure to be called as many times as you want without more memory being
allocated for the string list object each time. We have avoided a memory leak.
But in Delphi
applications, where you use the components (Buttons, Memos, Edits, etc.) you
drop on a form (at design time), you do not need to care to much about memory
management. Once the component is placed on a form, the form becomes its owner and
will free the memory taken by the component once the form is closed
(destroyed).
How to detect Memory Leaks?
Besides
writing memory safe code, preventing memory leaks can be done by using some of
the third-party tools available. Delphi Memory Leak Fix Tools help you catch
Delphi application errors such as memory corruption, memory leaks, memory
allocation errors, variable initialization errors, variable definition
conflicts, pointer errors. In order to
get detail information about the leaks we should set some Project compiler and
linker settings. In order for certain debug
features of FastMM4 to work you must make sure that certain debug switches are
turned on.
The following is a list of recommended switches:
a. In the Compiler options set the following
options
·
Debug Information
·
Reference Info
·
Use Debug DCUs
b. In the Linker options make sure that one of the following options is
set
·
TD32 Debug info
·
Map file
Different ways to find memory leaks in
our project
1. System.ReportMemoryLeaksOnShutdown
In latest Delphi
versions 2006 or later we can set ReportMemoryLeaksOnShutdown
= True before
Application.Run code in Project file to report memory leaks on shutdown.
When this global variable is
set to True, the Memory Manager will scan the memory pool when it shuts
down and report all unregistered memory leaks in a message dialog. The default
value is ReportMemoryLeaksOnShutdown is False.
begin
Application.Initialize;
Application.MainFormOnTaskbar := True;
Application.CreateForm(TForm1, Form1);
System.ReportMemoryLeaksOnShutdown := True;
Application.Run;
end.
2. FASTMM
FastMM is actually the memory manager included with Delphi since a few
years. It is an original work of Pierre le Riche. Although included with
Delphi, you can download full source code from SourceForge at http://fastmm.sourceforge.net. To take
fully advantage of FastMM feature, you should open FastMM4Options.inc file and
change some conditional compilation directive. This file is heavily commented
to explain all the symbols. When developing your application, you should define
the symbols "EnableMemoryLeakReporting"
and "FullDebugMode".
3. Memcheck
MemCheck
hunts memory leaks, memory corruption, use of an object after its destroying,
method calls on interface references which refer to a destroyed object, etc.
MemCheck is freeware, with source.
4. MadExcept
madExcept
was built to help you locating crashes in your Delphi application. Whenever
there's a crash/exception in your program, madExcept will automatically catch
it, analyze it, collect lots of useful information, and give the end user the
possibility to send you a full bug report.
5. Eurekalog
EurekaLog
is the new add-in tool that gives to your Delphi/C++Builder application (GUI,
Console, Web, ...) the power to catch every exception, generating a detailed
log of call stack (with unit, class, method and line #), showing and sending it
back to you via email or Web message (HTTP-S/FTP). Following an example of alert caused by TStringList.Create;
…and
an example of alert caused by TButton.Create(nil);
How to avoid Memory Leaks?
To avoid memory leak
in our application we have to write codes to free memory allocated to the objects
in our application. Whenever you need to use (create) an object in Delphi, you
need to free the memory it consumed (once no longer needed). We can use of the try
/ finally block to free the created
objects. For example
procedure SaveStudentList;
var
slStudent : TStringList;
begin
slStudent : TStringList;
begin
try
// Create the TSTringList object
slStudent:= TStringList.Create; // This allocates object memory
// Load the students list into the string list
slStudent.Add(‘Jack’); // Expands the object memory size
slStudent.Add(‘Shawn); // Expands the object memory size
slStudent.Add(‘Steve’); // Expands the object memory size
// Create the TSTringList object
slStudent:= TStringList.Create; // This allocates object memory
// Load the students list into the string list
slStudent.Add(‘Jack’); // Expands the object memory size
slStudent.Add(‘Shawn); // Expands the object memory size
slStudent.Add(‘Steve’); // Expands the object memory size
slStudent.Add(‘Pete’); //
Expands the object memory size
//
Now prepare to save to file
slStudent.SavetoFile(‘C:\StudentList.txt’);
finally
slStudent.SavetoFile(‘C:\StudentList.txt’);
finally
// Free up the string list memory
FreeandNil(slStudent);
FreeandNil(slStudent);
End;
end;
end;
Let's
say we are using text files in our application where the AssignFile method is
used to associate a file on a disk with a file variable, when you are finished
with the file, you must call CloseFile to free the file handle begin used. This
is where you do not have an explicit call to "Free".
var
F: TextFile;
S: string;
begin
AssignFile(F, 'c:\somefile.txt') ;
try
Readln(F, S) ;
finally
CloseFile(F) ;
end;
end;
F: TextFile;
S: string;
begin
AssignFile(F, 'c:\somefile.txt') ;
try
Readln(F, S) ;
finally
CloseFile(F) ;
end;
end;
Another
example includes loading external DLLs from
your code. Whenever you use LoadLibrary, you must call FreeLibrary:
var
dllHandle : THandle;
begin
dllHandle := Loadlibrary('MyLibrary.DLL') ;
//do something with this DLL
if dllHandle <> 0 then FreeLibrary(dllHandle) ;
end;
begin
dllHandle := Loadlibrary('MyLibrary.DLL') ;
//do something with this DLL
if dllHandle <> 0 then FreeLibrary(dllHandle) ;
end;
Hi! Good list of good tools. Would you mind to add Deleaker as it supports Delphi as well: https://www.youtube.com/watch?v=suyJfoSeRrQ
ReplyDelete