2
votes

I just can't figure out this memory leak that EurekaLog is reporting for my program. I'm using Delphi 2009. Here it is:

Memory Leak: Type=Data; Total size=26; Count=1;
The stack is:
System.pas  _UStrSetLength  17477
System.pas  _UStrCat           17572
Process.pas  InputGedcomFile  1145

That is all there is in the stack. EurekaLog is pointing me to the location where the memory that was not released was first allocated. According to it, the line in my program is line 1145 of InputGedcomFile. That line is:

CurStruct0Key := 'HEAD' + Level0Key;

where CurStruct0Key and Level0Key are simply defined in the procedure as local variables that should be dynamically handled by the Delphi memory manager when entering and leaving the procedure:

var CurStruct0Key, Level0Key: string;

So now I look at the _UStrCat procedure in the System Unit. Line 17572 is:

CALL    _UStrSetLength  // Set length of Dest

and I go to the _UStrSetLength procedure in the System Unit, and the relevant lines are:

@@isUnicode:
        CMP     [EAX-skew].StrRec.refCnt,1 // !!! MT safety
        JNE     @@copyString  // not unique, so copy

        SUB     EAX,rOff      // Offset EAX "S" to start of memory block
        ADD     EDX,EDX       // Double length to get size
        JO      @@overflow
        ADD     EDX,rOff+2    // Add string rec size
        JO      @@overflow
        PUSH    EAX           // Put S on stack
        MOV     EAX,ESP       // to pass by reference
        CALL    _ReallocMem
        POP     EAX
        ADD     EAX,rOff      // Readjust
        MOV     [EBX],EAX     // Store
        MOV     [EAX-skew].StrRec.length,ESI
        MOV     WORD PTR [EAX+ESI*2],0 // Null terminate
        TEST    EDI,EDI       // Was a temp created?
        JZ      @@exit
        PUSH    EDI
        MOV     EAX,ESP
        CALL    _LStrClr
        POP     EDI
        JMP     @@exit

where line 17477 is the "CALL _ReallocMem" line.

So then what is the memory leak? Surely a simple concatenate of a string constant to a local string variable should not be causing a memory leak.

Why is EurekaLog pointing me to the ReallocMem line in a _UStrSetLength routine that is part of Delphi?

This is Delphi 2009 and I am using the new unicode strings.

Any help or explanation here will be much appreciated.


Solution found:

The string was being assigned to an attribute of a new menu item. The menu item was added to the menu. Then the new menu item was freed, and thus I thought everything was cleaned up. Because the string (via reference counting) was still being used because it was copied to the item, the string was not freed in memory, even though it was introduced as a local variable in the routine.

Normally this shouldn't have been a leak. However, my program at times deletes various menu items with "menu.item.delete(i)". What I didn't realize was that the delete does not free the memory of the item itself. Therefore that reference-counted string did not get freed, thus causing the leak.

The solution was to change my "menu.item.delete(i)" statements into: "menu.item[i].free".

3
is this the only leak in the report?mjn
No there's quite a few others. But the majority of them are like this one, that seem to point to that _ReallocMem as being a problem. I've given this particular one as an example because I don't see how my code could be causing this.lkessler
There's a good way to go about tracking down memory leaks, and it's the opposite of what you're doing. Don't look at the type of variable you're leaking the most of, look at the type you're leaking the least of, because it's very likely that it will own a bunch of the other things you're leaking. Fix that leak first, and see how much smaller your memory leak list gets.Mason Wheeler
Seems like there's an "art" to leak plugging. I'll have to start with still art first and then move to portraits.lkessler
Nah. There's a lot of art involved in programming, but leak tracking is an exact science. The fundamental rules are that every piece of memory should have exactly one owner (either a data structure such as an object, or a reference counting system, or a block of code), and that the owner is responsible for cleaning up all the variables it owns when they're no longer needed. From those two rules you can derive the principle of starting as high up the ownership tree as possible with just a bit of analysis. :)Mason Wheeler

3 Answers

12
votes

It's pointing you to the line where the memory in question was allocated. And you're right, disposing of the string ought to be handled by the compiler. You've probably got one of two things going wrong.

  1. That's not the only thing you're leaking. The string belongs to an object which is never getting cleaned up. For example, CurStruct0Key may get passed to a constructor where it gets assigned to an object. Or
  2. You're doing something screwy with the string, such as casting it to a pointer and passing it around. Don't do this! It can b0rk the reference counting!

Check to see if either of these things are the cause of your problem.

6
votes

Get a better tool like AQTime and your "finding memory leak" issues will get simpler. In general I try to find all class leaks (TSomething) and ignore String type data because as someone above pointed out, the string doesn't get freed if it's referenced by some leaked record or class type. Sort out your classes and your records and the string leaks will all be fixed for you.

0
votes

I was experiiencing a memory leak in my Delphi project. This procedure worked on my machine beautifully but was faily on logged in machines on a network.

procedure accTrimWorkingSet;
var
 hProcess: THandle;
begin
 hProcess:=OpenProcess(PROCESS_SET_QUOTA, false, GetCurrentProcessId);
 try
   SetProcessWorkingSetSize(hProcess, $FFFFFFFF, $FFFFFFFF);
 finally CloseHandle(hProcess); end;
end;

It turned out that I was using a mapped dive. When I changed to a UNC path, the procedure worked.

Hope this helps.