Delphi application leaking AnsiStrings
You don't need to be explicitly allocating strings. Apart from mangling with reference counts, string fields of objects or records may also leak. For instance,
type
PRecord = ^TRecord;
TRecord = record
S: string;
end;
procedure TForm1.Button4Click(Sender: TObject);
var
r: PRecord;
begin
GetMem(r, SizeOf(r^));
Initialize(r^);
r.S := ' ';
FreeMem(r);
In the above example, since the memory of the record itself is freed, FastMM will report only the leaked string.
In any case, FastMM not showing a stack trace in the dialog does not mean that it lacks that information. Be sure to have FullDebugMode
, LogMemoryLeakDetailToFile
and LogErrorsToFile
are defined in 'FastMM4Options.inc'. Then look for a '[ExecutableName]_MemoryManager_EventLog.txt' file in the directory of the executable.
For the above example, FastMM produces the following file:
--------------------------------2012/5/27 4:34:46-------------------------------- A memory block has been leaked. The size is: 12 Stack trace of when this block was allocated (return addresses): 40305E 404B5D 404AF0 45C47B 43D726 42B0C3 42B1C1 43D21E 76C4702C [GetWindowLongW] 77AE3CC3 [Unknown function at RtlImageNtHeader] The block is currently used for an object of class: Unknown The allocation number is: 484 Current memory dump of 256 bytes starting at pointer address 7EF8DEF8: 01 00 00 ... ...
Now you can run the application, pause it and then search for the addresses. For the above log and the test application, the addresses resolves to:
Stack trace of when this block was allocated (return addresses): 40305E -> _GetMem 404B5D -> _NewAnsiString 404AF0 -> _LStrAsg 45C47B -> TForm1.Button4Click (on FreeMem line) 43D726 -> TControl.Click ...
edit:
Instead of manually looking up addresses, generate a detailed map file through linker options and FastMM will do it (thanks to Mason's comment).
Your edit on the question reflects a quite similar leak like the one in the above example. If the 'fList' is a regular TList
, it just holds pointers and have no knowledge of what those pointers points to. Hence when you dispose the pointer, just the memory allocated for the pointer itself is freed, not the fields of the record. So the leaks have nothing to do constants passed to functions but is like the pattern below:
var
assembleditem: PCodeIns;
p: Pointer;
begin
new(assembleditem);
assembleditem^.Caption:='a';
..
p := assembleditem;
Dispose(p);
For the record to be disposed, the code should typecast the pointer to its type:
Dispose(PCodeIns(p));
So your 'TCodeInsList.Destroy' should be:
destructor TCodeInsList.Destroy;
var
I: integer;
begin
for I := 0 to fList.Count - 1 do
Dispose(PCodeIns(fList[I]));
fList.Free;
inherited Destroy;
end;
In the end, the pattern you're looking for seems to be looking for places where the code intents to free records (less likely objects) having string fields. Looking for Dispose
, a little less likely FreeMem
, even less likely FreeInstance
to free memory of objects/records that FastMM shows as the allocated memory is leaked could help.
You're right that strings should be cleaned up automatically. I've seen a few ways to screw that up, though.
The first is if you're doing things with the string data structure directly that can break the reference count. This is the most likely, with the number of strings you're leaking.
The other one is calling Halt and leaving string references on the stack. But you're not going to leave 40,000 string references on the stack, so I'd look for code that gets passed a string and then fiddles with its reference count.