Memory Management & Ownership

Delphi's mobile compilers once used ARC (automatic reference counting) for objects (XE4–10.3); Delphi 10.4 removed it, so all Delphi platforms and FPC now use the same manual model for objects.

34.1 Who frees what?

Managed automatically?

Ordinals, reals, static arrays, plain records stack / inline storage string, dynamic arrays reference counting (strings: copy-on-write) reference counting via _AddRef/_Release Records containing managed fields compiler-generated initialize/finalize Class instances you must Free them (or delegate to an owner) New/GetMem memory

Delphi's mobile compilers once used ARC (automatic reference counting) for objects (XE4–10.3); Delphi 10.4 removed it, so all Delphi platforms and FPC now use the same manual model for objects.

34.2 Ownership patterns

Local lifetime — create, try, use, finally, Free.

Owner field — a class creates objects in its constructor and frees them in its destructor.

Owning containers — TObjectList<T> (OwnsObjects = True), TObjectDictionary<K,V> with [doOwnsValues], TStringList.OwnsObjects := True.

Component ownership — TComponent.Create(AOwner): the owner frees all owned components when it is destroyed. Forms own their controls; Application owns auto-created forms.

Interfaces — lifetime by reference counting.

type
  TInvoice = class
  private
    FLines: TObjectList<TInvoiceLine>;     // owns its lines
    FCustomer: TCustomer;                  // NOT owned: just a reference
  public
    constructor Create(ACustomer: TCustomer);
    destructor Destroy; override;
  end;

constructor TInvoice.Create(ACustomer: TCustomer);
begin
  inherited Create;
  FCustomer := ACustomer;
  FLines := TObjectList<TInvoiceLine>.Create(True);
end;

destructor TInvoice.Destroy;
begin
  FLines.Free;          // frees every line too
  inherited Destroy;    // FCustomer is freed by whoever owns it
end;

34.3 Multiple objects in one try/finally

var
  A, B: TStringList;
begin
  A := nil;
  B := nil;
  try
    A := TStringList.Create;
    B := TStringList.Create;
    // ... use A and B ...
  finally
    B.Free;           // Free is safe on nil
    A.Free;
  end;
end;

34.4 Free, Destroy, and FreeAndNil

Obj.Free checks for nil, then calls the virtual destructor. Never call Destroy directly.

FreeAndNil(Obj) frees and sets the variable to nil — use it for fields that may be checked later (if Assigned(FCache) then ...).

Freeing an object does not set other references to nil. Use TComponent.FreeNotification (components) or design single ownership.

34.5 Leak detection

compile with -gh (Lazarus: *Project Options → Debugging → Use Heaptrc unit*); add -gl for line numbers; report printed at exit (SetHeapTraceOutput('heap.trc') to write to a file)

valgrind --leak-check=full./app (compile with -gv) FastMM4 (built into Delphi since 2006)

ReportMemoryLeaksOnShutdown:= True; in the project file FastMM4 full debug mode / FastMM5 detects leaks, use-after-free, buffer overruns, with stack traces Deleaker, EurekaLog, madExcept commercial leak/exception tools

program LeakCheck;
{$IFDEF FPC}{$mode objfpc}{$H+}{$ENDIF}
uses Classes;
var
  SL: TStringList;
begin
  {$IFNDEF FPC}
  ReportMemoryLeaksOnShutdown := True;   // Delphi
  {$ENDIF}
  SL := TStringList.Create;
  SL.Add('leaked');                     // oops: no Free -> reported at exit
end.

34.6 A simple smart pointer using an interface

Libraries such as Spring4D (Shared<T>) and custom managed records (Delphi 10.4+, FPC 3.2 management operators) provide more complete smart pointers.

type
  TAutoFree = class(TInterfacedObject)
  private
    FObj: TObject;
  public
    constructor Create(AObj: TObject);
    destructor Destroy; override;
  end;

constructor TAutoFree.Create(AObj: TObject);
begin
  inherited Create;
  FObj := AObj;
end;

destructor TAutoFree.Destroy;
begin
  FObj.Free;
  inherited Destroy;
end;

procedure Demo;
var
  SL: TStringList;
  Guard: IInterface;
begin
  SL := TStringList.Create;
  Guard := TAutoFree.Create(SL);   // SL is freed when Guard goes out of scope
  SL.Add('no try/finally needed');
end;