There are lots of material out there covering different topics on how to detect memory leaks in Delphi applications, using different tools, but I failed to find a complete guide. So here is my attempt on this subject.
First you need to install FastMM. I strongly suggest installing the latest release of FastMM 4 branch, which is version 4.992. Fortunately, IntraWeb users can just select the option to install FastMM4 from IntraWeb install program and it will be installed. In any case, you can always grab it from the main repository in Github: https://github.com/pleriche/FastMM4
Have in mind that the FastMM4 options from the version installed with IntraWeb are slightly different compared to the standard one. Especially because it is targeted to be used in multi-threaded applications, not in VCL desktop ones.
Our release also includes a free application named FastMM4 Options Interface, developed by JED Software (https://jedqc.blogspot.com/2007/07/new-fastmm4-options-interface.html), which is very handy to change the various settings of FastMM without getting your hands dirty with confusing compiler defines inside FastMM4Options.inc file – where all FastMM 4 options are stored.
So, let’s get down to it:
This is the main interface. The first time you run it you must load the FastMM4Options.inc file from the folder where FastMM 4 is.
IntraWeb comes with settings especially configured to give you the best performance in production. So, we need to change it a little for debugging, especially to detect memory leaks.
First, switch to the “Debugging” tab and check the options “FullDebugMode” as shown below:
This is all that is required to get it going. However, I also recommend another setting for debugging, especially if memory leaks are involved:
This setting instructs FastMM 4 to clear each allocated block of memory before returning it. This is extremely helpful to identify memory corruption or use of dangling pointers after objects have been freed. You can track down some difficult to reproduce bugs with this setting.
Now, all you have to do is click on the Save button. A new version of FastMM4Options.inc file will be saved in the same folder.
First you need to explicitly include FastMM4 in your DPR file (see picture below). Add it as the first unit in your project (you can include the whole path to it, in case you have more than one FastMM in your library path – which is not unusual at all).
Now, open the project options and make sure that Debug information is checked. Also good to check the option to “Use Debug .dcus”:
Make sure to also check the “Debug Information” setting under Linking options:
Make sure that the selected platform matches your project’s target platform (i.e., Windows 32-bit or Windows 64-bit platform).
Now you must copy the FastMM 4 auxiliary debug DLLs to your application bin folder (the folder where your EXE file is)
These DLLs are named FastMM_FullDebugMode.dll and FastMM_FullDebugMode64.dll depending on your target platform. You can find them inside subfolder.
in your FastMM root folder.
So, in our bin folder of our test project, we have something like this:
Please notice that I usually copy both (32-bit and 64-bit) DLLs to the application folder, even though only one is necessary. You can copy only the one you will need, or both. It doesn’t really matter.
The test application is really simple. Download it here.
We basically force a memory leak of a TStringList instance (plus all the strings that it contains). You must run the application and click on the button at least once to create a memory leak.
Go ahead, build the application and run it (debugging it) from within Delphi IDE.
Please notice that if you get an error message like this:
It means that the FullDebugMode DLL wasn’t found by FastMM. Please copy the appropriate version of FullDebugMode DLL to the same folder where your EXE file is.
If your setup is correct, the application will run.
Now, click on the “Leak Memory” button only once and close the application.
Open the folder where your executable is in Windows Explorer. You will notice that there is a new file in there:
This file is the log file, generated by FastMM4 which contains detailed information regarding all the memory leaks found during your application execution.
A few things to have in mind when dealing with this log file:
– Depending on the number of memory leaks, it can be huge (I’ve seen some around 100 Mb and more)
– It can take some time to write all these information to the log file (up to a few minutes), and you should not interrupt/kill the process
– FastMM can only detect leaks that actually occurred, meaning that it doesn’t identify potential coding problems, but only memory that has actually allocated but never deallocated. It’s is not a static code analysis tool.
– Our FastMM4Options.inc file is configured to clear the log file each time the program runs. You can change this setting but the log file tends to become difficult to read. If you need to keep the files being generated, copy them to a different folder or rename them after each run.
– The more you test your real application using FullDebugMode the more likely you will find memory leaks. However, you should start small. Don’t try to test everything in one pass and end up with a 1 GB log file which is impossible to analyze. Test small parts of the application at each execution. Create a test plan which covers all areas of the application, but split this test into small manageable tasks.
– NEVER, and I mean NEVER NEVER NEVER use an application built with FullDebugMode settings in production, unless you are doing a limited/controlled test and are also aware of all the performance penalties. The memory consumption grows exponentially, the application performance will be really poor and some weird errors can happen (not due to FastMM4 itself, but because application errors – e.g. a memory corruption – will have different outcomes when FullDebugMode is active).
Understanding the memory leak log file
Now, the hard part: Analyze and decipher all the information contained in the FastMM log file. There are three different steps involved: Find where the leak happens, understand why it happens, and find out how to fix it.
It can take a long time before you can master the techniques involved here. Sometimes it feels like a mix of technical knowledge, experience, luck and black magic. But getting the basics right will get you going and will probably deliver good results.
WHERE it happens
Relatively straightforward. First open the log file and go straight to the bottom. You will find something like this:
This application has leaked memory. The small block leaks are (excluding expected leaks registered by pointer):
13 – 20 bytes: UnicodeString x 1
37 – 52 bytes: UnicodeString x 3, Unknown x 1
69 – 84 bytes: TStringList x 1
This is the summary of all the leaks found. You can see that we basically have 1 instance of a TStringList object, + 4 unicode strings + 1 unknown object.
In front of the description of each leak you also have the size of the block leaked. FastMM will order them using the size from the smallest to the largest.
Here is where the experience and the knowledge of the particular project makes a huge difference. Most of the time, a single memory leak is responsible for all other leaks, meaning that when you fix the main leak, all other leaks will be fixed at the same time. The memory leaks usually happen in the form of one or more graphs. For example, a (e.g. VCL) form is responsible for freeing all the owned controls (buttons, labels and all visual controls in it). If you fail to free a form you will probably see dozens or even hundreds of leaks in the leak report, but they are all consequences of the form leakage. If you fix it and free the form, it will also free all owned controls and all leaks will be gone at once. Another example is a DataSet. If a single DataSet leaks you will also find several other associated leaks (Fields, Parameters, TList instances and all sorts of objects used internally by a TDataSet descendant).
Thus, you should always start from the object that is likely to be responsible for others. If you find a TField leak, probably it was caused by a TDataSet leak. And this TDataSet leak can be also caused by another leak (a TDataModule or even a TForm). Spend a few minutes trying to imagine which one can be the cause of the others. Then start from it.
Back to our example, it is more or less obvious that the TStringList is probably the major leak here. So we start from it.
In our FastMM log file we need to find what FastMM has to say about this TStringList instance
Hint: Search for “class: TStringList” within the file and you will find it:
Here, FastMM give us lots of information about this leak:
– The time stamp when it occurred
– The size of the leaked block in bytes
– The call stack when the leak happened, including line numbers
– The class of the object
– The allocation number of FastMM 4 (this is basically a sequential number)
– The memory dump of the address of the object
At first you can safely ignore most things here except the call stack. The call stack will show us exactly where the leak happens inside our application.
Sometimes it’s hard to identify where exactly the leak is, but in general you should focus on your own code included in the call stack and temporarily ignore the VCL/3rd party libs code (unless the leak is inside one of these other units). In general we should assume that the leak is caused by our own code. In this specific case we have a very good indication.
is clearly our own code. The number in square brackets  shows the line number.
If you are not familiar with how a call stack works, you need at least to understand that the stack shows the methods executed in sequence. The last method executed, before the allocation that leaked, is on top of the stack and the first method executed (of that sequence) is at the bottom of the stack. Sometimes the location of the leak in quite obvious, but sometimes it requires that you go through each one of the functions and methods present in the call stack to have any clue about its origin.
Back to our source code we see that it makes perfect sense. Inside Unit1.pas, line 30 we create an instance of the class TStringList and, guess what, we forgot to free it 🙂
WHY it happens
This one is quite simple to answer in our example. We created a new instance of TStringList but we forgot to free it. Once it is a local variable, it is rather straightforward to understand why FastMM4 reports this as a memory leak.
Have in mind that this is a very simplified example and sometimes can be hard to identify why some instances are not freed. For instance, mixing objects and interfaces may create some very complex scenarios which can be a nightmare to fully understand.
HOW to fix it
In our simple case it’s also very simple to fix it. We just need to enclose that block in a try..finally and free our TStringList instance.
Testing our fix
Now, build and run the application again.
Hint: never just compile, always build when dealing with memory leaks. It can save you lots of time hunting ghost leaks.
You will see that the log file is gone now:
Remember that our FastMM4 config is prepared to delete the old file during startup. Once there are no leaks, no file is generated.
After the fix is done, revert the FastMM4 options settings (as described in FastMM 4 Setup topic above).
Some final tips
1) It is a good idea to force a memory leak – when testing for memory leaks – just to make sure that FastMM will always create a log file for your application. I usually do this:
which will generate this output in my log file:
This way I know that everything is working as it should (the application setup + FastMM4 options). When all memory leak investigation is done, I’ll just remove this line from my project and it will be perfect.
2) Never underestimate a memory leak. All the memory leaks should be fixed and they can slowly erode all the memory available to your application, especially when dealing with multi-threaded server applications running 24×7, like IntraWeb applications. A single DataModule instance which leaks can cause your application to fail completely when you consider that hundreds or thousands of users may be using it.
3) Don’t try to fix all the leaks at once. If you never checked your application for memory leaks during development, it is very likely that you will find dozens or even hundreds of leaks. Don’t panic! Try to imagine the leak graph – as we explained above – and fix them one by one. You can have good progress in very short time working that way.
4) In multi-threaded applications (like IntraWeb servers) always start with a single user session but make sure to also test multiple sessions. Some problems will only occur when multiple sessions exist.
5) Never take for granted that your application is leak free. Some leaks only occur in very specific circumstances and even a highly tested application or framework can present some leak under very special conditions. These type of leaks are less important (because they are unlikely to cause the whole application failure), but nevertheless they should be fixed.
6) Memory leaks are also a good indication of design problems. Many times I found serious design flaws when fixing memory leaks, i.e. the poor design itself was the main cause of the leak. In this case, fixing the design generally also fixes the leak.
7) Whenever you extend or refactor your application, consider running some memory leak checks as described here. Never take for granted that your code is perfect and there are no leaks. Even the most experienced developers may eventually fail to release some allocated resource causing a memory leak.