delphimemory-managementreference-counting

Which memory management techniques exist?


I know of these three techniques:

  1. Manually

    uses
      System.Classes;
    
    procedure DoSomething;
    var
      sl: TStringList;
    begin
      sl := TStringList.Create;
      try
      finally
        sl.Free;  // Invoking destructor
      end;
    end;
    
  2. Reference counting / interface (TInterfacedObject)

    Fastest example from the top of my head in the standard library:
    uses
      Xml.XMLDoc, Xml.XMLIntf;
    
    procedure DoSomething;
    var
      xmldoc: IXMLDocument;
    begin
      xmldoc := TXMLDocument.Create(nil) as IXMLDocument;
    end;  // No reference to xmldoc anymore, freed automatically
    
  3. Ownership

    Like almost the whole VCLibrary or this one:
    uses
      System.Generics.Collections;
    
    procedure DoSomething;
    var
      ol: TObjectList<TObject>;
      i: Integer;
      o: TObject;
    begin
      ol := TObjectList<TObject>.Create(true); // the list takes ownership of the objects
      try
        for i := 0 to 9 do begin
          o := TObject.Create;
          ol.Add(o);
        end;
      finally
        ol.Free; // does not only free the list but all objects in the list, too
      end;
    end;
    

Are there more?


Solution

  • When it comes to memory management models for managing object instances in Delphi, there are two: manual memory management and automatic reference counting. All object instances will be released either manually or through reference counting mechanism.


    But when it comes to actual coding patterns, there is number of ways we can write the code in order to trigger the release of an object instance and it is almost impossible to list and categorize them all.

    The best way to illustrate the complexity involved is by asking additional question:

    What do you consider as a memory management technique?

    For instance, manually releasing object instance requires invoking the destructor. But there are several commonly used ways to do so: by calling Free, Destroy, or FreeAndNil. But, Free and FreeAndNil eventually will call Destroy. So the question is, should we consider that those different methods of invoking the destructor are the same technique or different techniques? What about other custom written methods that will trigger destruction of an object instance?

    When it comes to releasing reference counted object instance, your example has shown indirect way of release - just letting reference go out of scope. But there is an additional way to release such object instance, and that is by explicitly assigning nil to such reference.

    procedure DoSomething;
    var
      xmldoc: IXMLDocument;
    begin
      xmldoc := TXMLDocument.Create(nil) as IXMLDocument;
      ...
      xmldoc := nil;
      ...
    end; 
    

    Again, the question is whether we consider those two different examples as the same or different?

    When it comes to ownership, it is just a way to delegate releasing an object instance to some other entity. At the end, in case of manually managed object instances some code at some point will directly invoke destructor on such object. While this is clearly a different coding pattern than directly invoking destructor on object reference without going through additional layers of indirection, at the end the instance will be released manually.

    We can also transfer ownership of reference counted object instances. If you have a collection that holds interface references, then this can also be considered as ownership transfer, as release of those instances will depend on the release of the collection itself, even though involved code will not directly call destructor, but will rely on automatic reference counting to do so.

    The next question that arises is: What about fields? Your first example shows construction and destruction of local object instance. If we have an object field in a class and manually call Free to such field in its destructor, should we consider that as a manual technique or ownership transfer, because actual release of that inner object instance will depend on the release of its outer, owning object.

    There is additional aspect to reference counting. While compiler automatically inserts reference counting code (calls to _AddRef and _Release methods) in appropriate places, the _Release method itself will have to directly call the destructor to actually free the instance. In a way this is just another example of ownership transfer, with some help of the compiler.


    From one perspective, we can say that those three techniques you have mentioned are the (two) three basic techniques to release an object instance. But one the other hand, there is an infinite number of them.