How to find memory leaks in Delphi with the help of Deleaker

This tutorial demonstrates how to detect memory leaks and other resource leaks in applications written in Delphi.

Deleaker can be used either as a standalone application or as an extension for RAD Studio. Deleaker Standalone is useful, for example, when RAD Studio is not installed on the target machine.

When used as an extension, Deleaker allows developers to search for leaks without leaving RAD Studio, making it easier to navigate directly to the source of potential issues.

After installation, a new Deleaker menu item is added to the RAD Studio main menu:

To open the Deleaker window, select Deleaker -> Deleaker Window:

Deleaker Window in Delphi

Adding a memory leak

Create a simple application with a single form, add a button, and introduce a leak by allocating memory and creating an object in the button click handler:

  procedure TForm1.Button1Click(Sender: TObject);
  var
    p: Pointer;
    StringList: TStringList;
  begin
    GetMem(p, 42);
    StringList := TStringList.Create;
  end;

Project setup

To obtain the most reliable and complete information about leaks, it is important to configure the project options properly.

Enable debug information

First, ensure that both the compiler and the linker generate debug information. This information allows Deleaker to resolve call stack addresses and determine the source file and line number where a resource was allocated.

To enable debug information for the compiler, open Project -> Project Options, then navigate to Building -> Delphi Compiler -> Compiling and set Code generation -> Debug information to Debug information.

To enable debug information for the linker, open Project -> Project Options, navigate to Building -> Delphi Compiler -> Linking, and set Debug information to True:

Make the compiler generate debug information in Delphi

If the project is built from the command line, use the compiler switch -V.

Stack frame generation

It is recommended to enable stack frame generation.

Deleaker works with code compiled without stack frames; however, when stack frames are enabled, the collected call stack information is more complete and easier to analyze.

To enable stack frames, open Project -> Project Options, navigate to Building -> Delphi Compiler -> Compiling, and set Code generation -> Stack frames to True:

Enable stack frames in Delphi

When building from the command line, use the compiler switch -$W+.

Disable optimization

More accurate and readable leak information can be obtained by disabling compiler optimizations.

Open Project -> Project Options, navigate to Building -> Delphi Compiler -> Compiling, set Code generation -> Optimization to False, and Debugging -> Use debug .dcus to True:

Disable optimization in Delphi

When building from the command line, use the compiler switch -$O-.

Taking a snapshot

Start debugging and click the button to introduce the leak. Then return to RAD Studio, open Deleaker -> Deleaker Window, and click Take Snapshot to obtain a list of allocated resources, including memory, handles, GDI resources, and objects:

For each resource, you can inspect the call stack to understand where it was allocated. If source file and line number information is available, Deleaker opens the source code in the editor when you double-click an entry or use the context menu:

Viewing Delphi objects

On the Delphi Objects tab, objects are grouped by class. For each class, the list of created objects is displayed, and a full call stack is available for each object:

You can quickly locate a specific class using Filter by name by typing its name:

Getting a list of leaks

When the application exits, Deleaker automatically creates a snapshot containing all resources that were allocated but not freed. These resources are considered leaks.

By examining call stacks for each allocation and object, you can determine whether the resource must be explicitly freed:

What if a process consumes more and more memory?

In some cases, a process continuously increases its memory usage. Similarly, the number of handles or GDI objects may grow over time:

To identify the source of such leaks, add a timer that periodically allocates memory:

  procedure TForm1.Timer1Timer(Sender: TObject);
  var
    p: Pointer;
    StringList: TStringList;
  begin
    GetMem(p, 42);
    StringList := TStringList.Create;
  end;

Pay attention to "Hit Count"

Deleaker groups leaks by call stack. The Hit Count column shows how many allocations share the same call stack. If memory is repeatedly allocated at the same location, the Hit Count value continuously increases.

If objects of the same class are repeatedly created, the Object Count column increases in the Delphi Objects tab.

Sorting by Hit Count or Object Count quickly highlights problematic areas in the code:

Comparing consecutive snapshots

Sorting alone may not hide resources that are steadily accumulating. Another effective approach is to compare consecutive snapshots.

First, take a base snapshot. Then allow the process to run and allocate more resources, and take a second snapshot. Compare the two snapshots to see only allocations that were created after the base snapshot.

If the result is empty, clear all filters to display all allocations.

Filters

Deleaker filters out allocations that are unlikely to be caused by your application, such as allocations made by system libraries.

The Filters button displays how many leaks are shown and how many are hidden. You can enable or disable filters as needed:

The following filters are available:

Hide leaks with no source code
Hides leaks for which Deleaker could not determine source code information.

Hide leaks from excluded modules
Hides leaks originating from modules listed in Options -> Exceptions -> Excluded Modules. By default, this includes system libraries.

Hide known leaks
Hides leaks that are known and typically unavoidable, such as one-time allocations performed by standard libraries. The list of such leaks is available in Options -> Exceptions -> Known Leaks.