Memory Leak in Delphi and How to avoid it

What is Memory Leak
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.Add(‘Pete’);     // Expands the object memory size
    // Now prepare to save to file
   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);

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
  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
   slStudent.Add(‘Pete’);     // Expands the object memory size
    // Now prepare to save to file
   slStudent.SavetoFile(‘C:\StudentList.txt’);
  finally
   // Free up the string list memory
    FreeandNil(
slStudent);
  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;
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;

Comments

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

Post a Comment

Popular posts from this blog

ShellExecute in Delphi

How to send Email in Delphi?

Variants in Delphi. Use of Variant Array. How to check a Variant is unassigned or empty or clear ?