In computer science, a memory leak is a type of resource leak that occurs when a computer program incorrectly manages memory allocations.
One of the more challenging aspects of developing in a language without automatic memory management (i.e. a garbage collector or a referencing counting mechanism) is ensuring that your application does not leak memory or resources. In fact, even with the help of memory managers, leaks can and do occur. The results of a leak can range from mild to catastrophic. Avoiding a leak requires constant discipline and vigilance on the part of an often fatigued and stressed developer.
Memory leaks are a class of bugs where the application fails to release memory when no longer needed. Over time, memory leaks affect the performance of both the particular application as well as the operating system. A large leak might result in unacceptable response times due to excessive paging. Eventually the application as well as other parts of the operating system will experience failures.
Consider this report which states that Microsoft are investing in Rust to reduce memory related bugs which accounted for 70% of their yearly patches for the last 12 years. Leaks will happen. Finding a leak can be like looking for the proverbial needle in a haystack. This is where a good memory profiler is essential. In this post I will be evaluating Deleaker.
More often, it’s not a question of if your code leaks, but what and where.
What is Deleaker?
Deleaker is an IDE integrated memory profiler for Visual C++, .NET and Delphi, compatible with RAD Server and Visual Studio. It can also run as a standalone application and attach to executing processes. It supports both 32-bit and 64-bit applications and is capable of detecting both memory leaks as well as resource leaks like GDI leaks, Windows USER objects and handles etc. So not only can Deleaker identify leaks in your own code, it can track GDI resource leaks as well. Being a low level debugger, it is capable of detecting leaks within the IDE itself such as in design time components. To learn more about Deleaker’s features, visit the website here.
You can request a 14 day trial of Deleaker here. Once you have downloaded the software, installation is pretty straight forward and completes quickly.
Upon opening Deleaker for the first time, you will be prompted to select your default profiler mode: managed (.NET) or unmanaged (Native). You can change this later at anytime.
The first thing to note once the application opens is the help information which can be accessed via the main menu, or by pressing F1. For more detailed instructions see the online tutorial.
The Standalone Deleaker Window is essentially the same as what you see in the IDE, with an extra section used for selecting the process to profile, and a main menu.
The UI is divided into five logical sections:
- Profiling mode selection
- Debugging target selection and options
- Snapshot management
- Profiling results and filters
- Stack trace
Profiling Mode Selection
The Profiling Mode allows you to select managed or unmanaged mode. Selecting a profiling mode sets sensible default configuration options accordingly.
Debugging target selection and options
In this section, you can either execute a new command or attach to a running process. In this example, I chose to attach to a process which prompted me to select from a handily sorted list. Upon selecting a process, you are next asked to select which allocation types to monitor. You can change the profiling options and allocation types at any time.
This is where things get interesting. The best way to use a tool like Deleaker is to introduce it right at the beginning of development. That’s the easiest way to notice the introduction of a leak because the change scope is small. Deleaker allows you to save profiling results as snapshots which can be used for comparisons with later results, or simply to assert that at a particular point in time the code was leak free.
For Agile teams, snapshots could be produced and added to source control after each Sprint. This discipline would ensure leaks are addressed as high priority in the next Sprint and not simply treated as technical debt to be addressed at the end of the project; the consequences of which would make identifying and resolving such issues inordinately more complex and expensive, and potentially delaying a final release indefinitely.
A snapshot at the end of a Sprint is an excellent artefact to add to the metrics which measure the health of a project. Memory and resource leaks should never be ignored.
Profiling Results and Filters
Once the profiling information has been collected, Deleaker lists the results in this section.
Clicking on a reported leak or object allocation will display a stack trace in the following section. Various filters allow you to select what is displayed, you can choose which columns to view, and you can export the results to an XML file. Another interesting view is the Resource Usage Graph.
By clicking on the class name of an allocated object in the previous view, the stack trace is displayed. This gives you a detailed look at the call stack. When using Deleaker within the IDE, double-clicking on a method call takes you directly to the source code where the allocation was made. This is indispensable to identifying and resolving leaks.
Before trying to identify leaks in my current project, I decided to create a basic application to become more familiar with Deleaker’s functionality. The source code is available here.
To get the most out of Deleaker, there are several project options that should be set.
Right click on the project (i.e. DeleakerTest.exe), select project options, and under the Building > Delphi > Compiler ensure the following options are set:
- Code Generation > Optimization: false
- Code Generation > Stack frame: true
- Debugging > Debug information: Debug information
Refer to the following image for more details, or to the Deleaker guide here.
Once you are ready, run with debugging information by pressing F9. When the application has finished, Deleaker will collect the results for display.
Initially I ensured there were no leaks in my code, which Deleaker correctly confirmed:
I then deliberately introduced some common mistakes which cause memory leaks. Deleaker correctly identified all issues, thus giving me confidence in this tool:
As mentioned earlier, by clicking on a method call in the stack trace I am led directly to the line of code where a leaking object was allocated. This is the advantage of using Deleaker in the IDE:
Putting Deleaker to Work
Having had an opportunity to learn about Deleaker’s functionality and verify its analysis, I decided to run it against one of my own projects – one in which I have been careful to avoid memory leaks.
I’m currently working on a client-side Delphi XMPP library and, to help drive the design, I created a console application to allow the easy execution of commands against the library.
For this test, I modified the application to run as a bot, firing off a number of commands over one minute. Here is the bot in action, communicating with an XMPP Server and another XMPP client. You can see it going offline and online in Pidgin’s buddy list and the messages in the chat window.
Once the bot had finished, Deleaker collected the allocations and presented a summary of issues:
Oh dear, it looks like I have some work to do. I really wasn’t expecting to see that. As mentioned I was particularly diligent on this project, making sure that I had cleaned up all allocated objects. This is infrastructure code upon which an application will be built, it absolutely must not leak.
This exercise reiterated the point to me that no matter how careful you think you are, subtle issues will creep into your code, even with a fine language like Object Pascal. This is especially true as many developers code tired and under pressure. You really do need the assistance of good tooling.
I have found Deleaker to be extremely simple to use and highly effective in identifying leaks in my code. Not only can it identify issues in Delphi, but it can be used in Visual Studio to identify issues with .NET and Visual C++ projects. From now on, I will be using Deleaker in all of my projects.