'Delphi FastMM4 how read MemoryManager_EventLog.txt?

I have added FastMM4 to my project for detect a memory leak

program MyProg;

uses
  {$IFDEF DEBUG}
  FastMM4,
  {$ENDIF}
  ...other uses

on form close FastMM4 produce a MemoryManager_EventLog.txt, with all leak block.

This is an example:

    --------------------------------2019/6/19 9:49:25--------------------------------
A memory block has been leaked. The size is: 20

This block was allocated by thread 0x558, and the stack trace (return addresses) at the time was:
4075D2 [System.pas][System][@GetMem$qqri][4614]
40ABAF [System.pas][System][TObject.NewInstance$qqrv][16452]
40B3D6 [System.pas][System][@ClassCreate$qqrpvzc][17790]
40AD20 [System.pas][System][TObject.$bctr$qqrv][16516]
F3BEF6 [NdST.pas][NdST][TFinder.$bctr$qqrp14System.TObject][1011]
11157D1 [SuperNode.pas][SuperNode][TSuperNode.$bctr$qqrp14System.TObject][993]
D1D840 [Network.pas][Network][TNetwork.Create$qqrv][968]
F3A70D [NdST.pas][NdST][TNdST.$bctr$qqrp14System.TObject][177]
F3B658 [NdST.pas][NdST][TNdSTCAP.$bctr$qqrp14System.TObject][652]
10A4CCA [Print.pas][Print][TPrint.CreateCollection$qqrv][4274]
10A2773 [Print.pas][Print][TPrint.CreateItem$qqrv][3031]

The block is currently used for an object of class: TList

The allocation number is: 53883

Current memory dump of 256 bytes starting at pointer address 7EBA5360:
48 FA 49 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 7C C4 2D 79 00 00 00 00 E0 4B BA 7E
00 00 00 00 00 00 00 00 9C CD 41 00 00 00 00 00 7C D2 00 00 D2 75 40 00 AF AB 40 00 D6 B3 40 00
1A 72 11 01 29 AD 40 00 F8 57 11 01 40 D8 D1 00 0D A7 F3 00 58 B6 F3 00 CA 4C 0A 01 73 27 0A 01
58 05 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
20 AD 40 00 B8 AB 40 00 D1 57 11 01 F4 1B 0B 01 22 4D 0A 01 87 27 0A 01 0A 48 11 01 52 6E 11 01
H  ú  I  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  |  Ä  -  y  .  .  .  .  à  K  º  ~
.  .  .  .  .  .  .  .  œ  Í  A  .  .  .  .  .  |  Ò  .  .  Ò  u  @  .  ¯  «  @  .  Ö  ³  @  .
.  r  .  .  )  ­  @  .  ø  W  .  .  @  Ø  Ñ  .  .  §  ó  .  X  ¶  ó  .  Ê  L  .  .  s  '  .  .
X  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .
.  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  x  ï  ï  †
<  -  .  .  À  F  ¼  ~  .  .  .  .  .  .  .  .  .  .  .  .  ‡  .  .  y  .  .  .  .  .  Q  º  ~
.  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  €  Ò  .  .  Ò  u  @  .  ¯  «  @  .  Ö  ³  @  .
   ­  @  .  ¸  «  @  .  Ñ  W  .  .  ô  .  .  .  "  M  .  .  ‡  '  .  .  .  H  .  .  R  n  .  .

The leak seem to be caused by TList, but I free every TList. Is TList.Free not sufficient?



Solution 1:[1]

If you create objects and save their references to TList, it is essential that you free them before the TList is freed.

for I := 0 to aList.Count-1 do
   anObjectType(aList[I]).Free;

If you want your objects to be freed automatically, use TObjectList with OwnsObjects parameter set. TObjectList frees the memory of all its objects when it is destroyed.

Solution 2:[2]

So, how to read a FastMM log?

The most relevant section is the one that states "the stack trace (return addresses) at the time was:" where you will see a listing like the one below. This is what is called a stack trace.

A stack trace shows the execution path of your program; which function called which function.

First, it is good to know that for each line in the log:

  • the first number is the memory addres where that function resides (EXE file)
  • the last number in brackets is the source code line number where the function resides (PAS file).

We start reading from the bottom.

So, we can see that all started in the Print.pas unit. The CreateItem method of TPrint object called CreateCollection which tried to create a new object. In the end (see the top lines) we end up in TObject's constructor. That construnctor is a class method: ClassCreate. TObject.NewInstance means that the TObject was actually being created. There, the constructor allocated (normally) some memory for the TObject via GetMem.

Now, until this point, nothing wrong has happened. We allocated some memory when we created an object. Not a big deal. The error happened when the programmer forgot to release that memory back. In other words, we created an object, and we never freed it.

Anyway, the log seems a bit weird because the stack trace shows some items being created.
So we need to go into the source code and check all places where we call TPrint.CreateItem. If you have just one place where TPrint.CreateItem is called, you are very lucky. You just have to check where that new item SHOULD be released, because it obviously it isn't. If you have just one place where TPrint.CreateItem is called, you are moderatelly lucky. You have to check EACH place.


However, then the log complaints that the TList itself is not released (does not complain about its items) - maybe the log is incomplete?

The block is currently used for an object of class: TList

So, releasing that TList should be enough. I guess, what happens here is that some external code takes control over the items and releases them, while TList itself is not released.


Remember, if you don't see a detailed stack trace, it means that you need to:

  • disable compiler optimizations
  • in the same page, enable stack frames
  • enable map file

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Charlie
Solution 2