did you use heaptrc to confirm that?
As this is a problem in the memory manager and heaptrc is registering a custom memory manager, this may not actually measure the same thing.
check memory usage in task manager
First, never trust your task manager about memory usage:
var
p: Pointer;
begin
p := Getmem(2000000000);
ReadLn;
end.
I've just allocated 2 gigabytes of memory but my task manager tells me that my program has only 1.8 megs.
Secondly, as jamie pointed out, the memory manager may not reuse previously allocated memory:
uses
heaptrc;
var
i: Integer;
p: PInteger;
begin
heaptrc.keepreleased := True; // Override reusing behavior
Write(GetFPCHeapStatus.CurrHeapUsed,' bytes => ');
for i := 0 to 10000 do
begin
new(p);
dispose(p);
end;
WriteLn(GetFPCHeapStatus.CurrHeapUsed);
end.
The memory manager can decide when and where to reuse memory from previous allocations. The example above uses the feature of heaptrc to completely disable any reuse, this is a special tool that allows easier detecting of use-after-free. No reuse allocation methods are also often used in security critical software like the sandboxing of browsers to ensure use-after-free cannot happen. Thats why browser alaways seem to be so memory hungry, it's simply because they make massive use of virtual memory and paging instead of reling on address reuse.
Note that while this above is of course forced through keepreleased of heaptrc, also the default memory manager does not always reuse memory. The reason for this is that the FPC memory manager is unlike the C memory manager not a first come first serve memory manager, but has different allocation lists for different sizes to avoid fragmentation:
var
p: PInteger;
sl: TStringList;
begin
Write(GetFPCHeapStatus.CurrHeapUsed,' bytes => ');
new(p);
dispose(p);
sl := TStringList.Create;
sl.Free;
WriteLn(GetFPCHeapStatus.CurrHeapUsed);
end.
Result:
As you can see it will keep some blocks allocated. The reason for this is that it allocates blocks for size 4 (Integers), as well as blocks for TStringList (and it's underlying string arrays), and keeps it around for those blocks, instead of just reuising the same blocks or freeing them completely.
As comparison see what happens when you use the C memory manager instead:
uses
cmem,
Classes;
var
p: PInteger;
sl: TStringList;
begin
Write(GetFPCHeapStatus.CurrHeapUsed,' bytes => ');
new(p);
dispose(p);
sl := TStringList.Create;
sl.Free;
WriteLn(GetFPCHeapStatus.CurrHeapUsed);
end.
Result:
This memory manager now does uses a first come first serve allocation method, meaning that instead of like before having two blocks allocated for different sizes and being kept around for the next allocations, now there is just one block which will be freed afterwards.
Neither of these two examples produces a memory leak.
Long story short, never make assumptions about memory leaks based on such simplified information like the allocation state. This is heaviely unreliable as it does not account for things like reuse policy, different allocation strategies, etc.
If you want to check for memory leaks, instead use tools like heaptrc (even though in this case it is not of much use as it overrides the memory manager), or valgrind