In Delphi XE, the following code will cause memory leak:
procedure TForm1.Button1Click(Sender: TObject);
var P, B: TProc;
begin
B := procedure
begin
end;
P := procedure
begin
B;
end;
end;
Run the code with
ReportMemoryLeaksOnShutdown := True;
and the memory manager prompt:
21-28 bytes: TForm1.Button1Click$ActRec x 1
A memory leak starts when a program requests a chunk of memory from the operating system for itself and its data. As a program operates, it sometimes needs more memory and makes an additional request.
Common Types of Memory Leaks. Leaks in managed platforms are effectively references to an element that is no longer necessary. There are many samples of this, but they all boil down to discarding said reference. The most common problem is caching.
When a programmer forgets to clear a memory allocated in heap memory, the memory leak occurs. It's a type of resource leak or wastage. When there is a memory leak in the application, the memory of the machine gets filled and slows down the performance of the machine.
Memory leak occurs when programmers create a memory in heap and forget to delete it. The consequences of memory leak is that it reduces the performance of the computer by reducing the amount of available memory.
This is due to the way anonymous methods work. Anonymous methods are implemented as TInterfacedObject descendants, and if you have more than one in the same routine, they end up as two methods of the same object. It uses interfaces for reference counting so you don't end up leaking the objects. However, if an anonymous method references itself, that ends up throwing off the reference count and causing a memory leak. What you're seeing here is caused by a combination of these two things.
This is a bug in the compiler (as far as I know). I opened QC83259 in Embarcadero's quality central about it.
You can work around this bug by creating the anonymous procedure in a routine. The following code won't leak.
procedure TForm1.Button1Click(Sender: TObject);
var P, B: TProc;
begin
B := GetMethod(); //Note, the "()" are necessary in this situation.
P := procedure
begin
B;
end;
end;
function TForm1.GetMethod: TProc;
begin
Result := procedure
begin
end;
end;
I know I'm 2 years late to this discussion, but I've recently run into this memory leak within our code and I couldn't get Ken's suggested answer to work. So with the help of a colleague of mine we came up with a different answer to keep using the nested anonymous methods yet avoid any memory leaks.
Below is an example of the solution we found:
procedure TForm1.Button1Click(Sender: TObject);
var P, B: TProc;
begin
B := procedure
begin
end;
P := procedure
begin
B;
end;
B := nil;
end;
My belief is that due to the way local variables are bound for the purpose of extending their lives in order for the anonymous method to use it outside of the scope it was created in, that it is making a copy of the underlying interfaced object in order to move the variable from the stack to the heap, and in doing so it is calling AddRef which increments the reference counter. Setting the variable to nil after it has been used calls the Release which in turn decrements the reference counter back down to 0 which allows the interfaced object to be freed.
After doing this we have not seen the memory leaks that were occurring from before.
Whether this is a bug or not, I cannot answer that, but I am interested in hearing the opinions from others. We see this as a way to allow us to continue using anonymous methods in a nested form like this.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With