Leaking Interfaced Objects in Delphi

I’ve been tracking down some memory leaks in a program written in the Delphi language (formerly known as Borland’s Object Pascal). I’ve run across this a few times over the years.

Interfaced objects in Delphi have the wonderful benefit of automatic lifetime management due to automatic reference counting. It’s almost as convenient as the automatic object disposal Java, C#, and Objective C (on Mac OS X Leopard).

There’s a potential memory leak, however if you descend from TInterfacedObject and override the AfterConstruction method. You must call inherited else the object will never be released!

How This Happens

Note that the constructor increments the reference count otherwise it will go to zero during construction under certain circumstances. That, of course, would cause the object to free itself immediately… and you the programmer would go an a man hunt to avenge the accelerated hair loss.

Because the constructor increases the reference count to prevent premature destruction, the reference count must also be decreased to its correct value after the constructor has finished executing. This happens in the AfterConstruction method. Look at the implementation in System.pas:

procedure TInterfacedObject.AfterConstruction;
begin
  // Release the constructor's implicit refcount
  InterlockedDecrement(FRefCount);
end;

Example

Say you descend from TInterfacedObject, and do something in AfterConstruction. The following example is simplistic, but makes the case:

type
  IFoo = interface
  end;

  TFoo = class(TInterfacedObject, IFoo)
  private
    Bar :Integer;
  public
    procedure AfterConstruction; override;
  end;
.
.
.
TFoo.AfterConstruction;
begin
  Bar := 1;
end;

Note that TFoo.AfterConstruction does not call inherited. Because inherited is not called, TInterfacedObject.AfterConstruction is not called, and the implicit reference count done in the constructor will never be undone.

Thus, the following code will never release the instance of TFoo:

var
  lFoo :IFoo;
begin
  lFoo := TFoo.Create;
end;

To fix this memory leak, call inherited in the descendant’s AfterConstruction definition, thus:

TFoo.AfterConstruction;
begin
  inherited; // <---- CALL inherited HERE!
  Bar := 1;
end;
This entry was posted in Programming and tagged , , , , , , . Bookmark the permalink.

Leave a Reply