Memory Leakage
MemCheck tutorial: find memory leaks
Hunting memory leaks is the main use and the initial motivation for MemCheck: in 1994, our project was on a reasonable size (about 1000 classes in 150 000 lines of code); yet we discovered that it consumed more and more memory as it was being used, due to memory leaks (this was on Turbo Pascal).
Thinking about it, we decided that it was not difficult and could be funny to write our own memory manager which would log each memory allocation and deallocation, to find out how much was lost. That was the basic idea. The very first version just reported something like "hey, there were 56 allocations which have not been freed, leading to 996 bytes lost" (ok, I won't say here how much it really reported the first time we ran it on OptIt - just that today we know for sure there are no memory leaks in the parts of the code we actually use).
Ok, let's go for a short demo of what MemCheck can do now...
- Launch Delphi
- In tools/Debugger options, ensure "stop on Delphi exceptions" is checked
- File/New application
- Close Unit1 without saving (this will be a formless app)
- Add the memcheck unit to the project
- In the project dpr file, just above the begin end, insert:
procedure Leak;
begin
TObject.Create;
end; - In the project's dpr file, make the body (begin...end part) contain
begin
MemChk;
Leak;
end. - Go to Project/Options/Compiler. Set everything for debugging (no optimization, stack frames, all debugging)
- Go to Project/Options/Linker. Include TD32 debug info.
- Build all
- If you run in the debugger, the program will execute and on termination, Delphi will show you where the leak is by raising an exception on the buggy line (this is the user interface of MemCheck: we just use Delphi itself !). The text file is less useful in this case.
- If you run outside the debugger, you'll use the text file to understand the leak (see below - in fact, today even with the debugger I prefer to use the text file).
- OK, let's improve a little... Add another procedure
procedure CallLeak;
begin
Leak;
end; - In the project's dpr file, make the body (begin...end part) contain
var
i: integer;
begin
MemChk;
for i:= 1 to 5 do
CallLeak;
end.
Reading the text file
In the example above, the text file tells you...
- There are a total of twenty bytes lost in memory leaks
- Five instances of TObject are lost; their size is four bytes; they were allocated in routine Leak, and the line number is given
- The routine Leak was called by CallLeak
- The routine CallLeak was called by the initialization
- MemCheck can not give any symbolic information for the next step in the call stack because Delphi did not generate any debugging information for these. In Fact, these two addresses are not in your program's code segment.
- The order in which the leaks occured is given
InsGet the call stack information in the debugger
Again, this was written before the debug info was available in the text file. So today this is less useful.
- Run the example above
- The debugger tells you there is a leak in the routine Leak
- Press Ctr-F7 and evaluate the global boolean variable ShowCallStack (it is defined in MemCheck). Change it to True.
- Press F9. The debugger shows you the calling line. You can go on seeing the stack...