Search code examples
delphidestructordynamic-memory-allocationdynamic-arrays

Will dynamic arrays in fields of Delphi objects be automatically deallocated/freed when the object is destroyed?


If I have a Delphi object containing a field with a dynamic array (containing for example strings), as follows:

   TClassWithDynArrayField = class(TObject)
   public
      some_dyn_array : array of string;
   end;

will this array (and/or the contents of it) be automatically deallocated/freed when the object is destroyed, or will I have to explicitly finalize it somehow in the object's destructor in order to avoid memory leaks or other nastyness (crashes in the garbage collector for strings and dynamic arrays, or other heap related code etc)?

If the answer is "yes, it will be automagically dellocated as soon as the object is destroyed, also leaving no inconsistencies whatsoever for the string/dynamic array garbage collection", will the same also be true for multi-level dynamic array fields? For example like this:

   TClassWithMultiLevelDynArrayField = class(TObject)
   public
      some_multi_level_dyn_array : array of array of string;
   end;

Would the answer be any different whatsoever if I instead used the "generic" way of defining these arrays (single level and/or multi-level) instead (which from what I've heard is internally equivalent to the "non-generic" definition of dynamic arrays, or isn't it?), as follows:

   TClassWithGenericMultiLevelDynArrayField = class(TObject)
   public
      some_generic_multi_level_dyn_array : TArray<TArray<string>>;
   end;

NOTE: For all these examples, please assume that other code will have arbitrarily populated all levels of the arrays before the object is destroyed.

The reason I'm asking this is that I often seem to get strange access violations when using dynamic arrays in objects (and dynamically allocated records), and also that there are some semi-related talk in the Delphi reference page about System.Finalize, seemingly differentiating the automatic deallocation of dynamic arrays and e.g. strings (i.e. in the case of Dispose-based deallocation, but I figured it could apply to object fields too?), as follows:

Finalize should be used only in Delphi code where a dynamically allocated variable is deallocated by other means than the Dispose procedure. Dynamic arrays can never be deallocated using the Dispose procedure, but can be freed by passing them to Finalize.

For global variables, local variables, objects, and dynamic variables deallocated using Dispose, the compiler generates code that finalizes all long strings, variants, and interfaces contained by the variable when the instance is destroyed.

If a dynamic variable meets the following two conditions, a call to Finalize is required to finalize the variable before it can be deallocated.

  • The variable is deallocated by other means than the Dispose standard procedure (for example, using FreeMem).

  • The variable contains long strings, variants, or interfaces, not all of which are empty or Unassigned.

Finalize simply sets all long strings to empty and all variants and interfaces to Unassigned, thus properly releasing any memory that was referenced by the long strings and variants.

Note that dynamic arrays are never mentioned in those cases where strings are mentioned, and also, dynamic arrays are mentioned as some kind of "manual" special case in the first sentence?

So, again, will dynamic arrays in fields of Delphi objects be automatically deallocated/freed when the object is destroyed, no matter how filled they are with e.g. strings, and no matter if they are arbitrarily many "levels deep" (i.e. dynamic arrays inside of dynamic arrays), as in my examples above?


Solution

  • Yes, dynamic-array fields are handled just like long strings and other compiler-managed variables, no matter now many levels deep.

    Their reference counts will be decremented in the object's destructor, and if the count is zero, the array will be disposed.