6

The following article on dynamic arrays in Delphi says that you allocate a dynamic array using the SetLength() function.

myObjects : array of MyObject;
...
SetLength(myObjects, 20);
// Do something with the array.
myObjects := nil;

http://delphi.about.com/od/beginners/a/arrays.htm

This seems like a memory leak to me:

The question is, if SetLength() is the equivalent of the C++ MyObject *obs = new MyObject[20], then arrays are just pointers, so is setting the Delphi myObjects variable to nil the same as setting obj = NULL in C++? I.e., is this a memory leak?

EDIT: I understand from David's answer that the compiler manages memory for dynamically allocated arrays. I also understand from his answer than the compiler does manage memory for ordinary class instances (hence the use of myObj := MyObject.Create and myObj.Free, myObj := nil, etc). Also, because Delphi classes (not records) are always allocated on the heap (Delphi using a kind of reference/pointer system), does that mean that all the objects within the (automatic memory-managed) dynamic array still need to be memory-managed by me? E.g., does the following cause a fault by double freeing a result?

myObjects : array of MyObject;
...
SetLength(myObjects, 20);
for i := 0 to 19 do
begin
  myObjects[i] := MyObject.Create;
end;
// Do something with the array.
// Before de-allocating it, if I *know* I am the only user of the array,
// I have to make sure I deallocate each object.
for i := 0 to 19 do
begin
  myObjects[i].Free;
  myObjects[i] := nil; // Redundant, but for illustrative purposes.
end;
myObjects := nil;
magnus
  • 4,031
  • 7
  • 26
  • 48

2 Answers2

8

Dynamic arrays are managed by the compiler. This is done by maintaining a reference count of all references to the array. When the last reference to the array is detached, the array is deallocated.

The documentation says:

Dynamic-array variables are implicitly pointers and are managed by the same reference-counting technique used for long strings. To deallocate a dynamic array, assign nil to a variable that references the array or pass the variable to Finalize; either of these methods disposes of the array, provided there are no other references to it. Dynamic arrays are automatically released when their reference-count drops to zero. Dynamic arrays of length 0 have the value nil. Do not apply the dereference operator (^) to a dynamic-array variable or pass it to the New or Dispose procedure.

In your example, assigning nil to your variable detaches the one and only reference and results in the array being deallocated. So there is no leak.

Delphi dynamic arrays are very different from C++ new. The closest analogue to that in Delphi is raw memory allocation with GetMem or New.


Your edit asks a different question. Instances of classes are not managed. They must be explicitly be freed. Your code does that. There is no double free because the compiler does not manage instances of classes.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • So are you saying that Delphi has basic garbage collection for arrays through the use of reference counting? (I'll assume the compiler reserves a few bytes for the integer holding the reference count.) If Delphi has GC for dynamic arrays, why doesn't it also have GC for classes? – magnus Feb 28 '14 at 04:03
  • 2
    @user Yes essentially. Although this is deterministic reference counting and not GC. The reference count (and the length) are stored in a meta data record immediately before the array payload. Objects pre-date dyn arrays. Mobile compilers do reference count objects with ARC. – David Heffernan Feb 28 '14 at 04:05
  • Yes, I'm familiar with ARC through Objective-C. – magnus Feb 28 '14 at 04:06
  • 1
    OK. Think of dynamic arrays as you would an ARC object in Obj-C. Delphi interfaces are managed in the same way. As are strings. – David Heffernan Feb 28 '14 at 04:10
  • Thank you for your answer David. Marked as correct. – magnus Feb 28 '14 at 05:58
  • 1
    By the way, I understand that my edit asks a different question. However, I only just recently found out that a `TStringList` class has an `OwnsObject` property which determines whether or not the contained objects are automatically freed on the destruction of the `TStringList` instance, and I was unsure whether this pattern applied to dynamic arrays as well. I needed clarity on both points, which your edited answer provided. Again, thank you. – magnus Feb 28 '14 at 06:05
  • Dynamic arrays are built-in types and not classes. Only the built-in types dynamic array, string, interface and variant are managed automatically. They do not free objects. That some classes in the runtime allow you to have them free contained objects is something totally different. – Rudy Velthuis Feb 28 '14 at 07:28
-3

Setting an object reference to nil does NOT free it. It simply decrements the internal reference count. When that reference count hits 0, the memory is freed or available for freeing.

So doing:

Obj.Free
Obj := nil;

isn't going to free it twice at all. It's going to free it once and set the Obj pointer to null.

For strings, the reference count used to be stored a an offset of 2 words before the first element and the size was stored 1 word before the first element. If it is constant, the reference count was usually -1. Not sure if this is still the case.

Brandon
  • 22,723
  • 11
  • 93
  • 186
  • Please define what you mean here by "object" – David Heffernan Feb 28 '14 at 04:44
  • You misunderstood my second question. I wasn't asking "Will `obj.Free` and `obj := nil` double free?". I was asking "Will `myObjs := nil` (assuming the reference count of the object pointed to by `myObjs` is 1) automatically call `Free` on each of the objects contained in the array?". – magnus Feb 28 '14 at 05:48
  • 3
    @user1420752 Yes and no. When your compilation target is a mobile platform, assigning NIL to an object reference will decrement the reference count, and once it reaches zero, the memory will be freed. This is called ARC (Automatic Reference Counting). When targeting desktop platforms (Win32/MacOS), there is no automatic reference counting of object references, so you'll need to free the object yourself. If you compile for multiple targets, just call Free and it'll work correctly in both mobile and desktop apps. – HeartWare Feb 28 '14 at 05:52
  • @user1420752 You're asking if each index in an array is an object on the heap, if it will be freed when the array is freed? The answer is NO because reference to the object at that index can be greater than 1 but references to the array itself can be 0 and will be freed. So again, the array will be freed but not each of its indexes unless each object in the array has a reference count of 0 as well. – Brandon Feb 28 '14 at 19:28
  • @CantChooseUsernames: What you say only applies to mobile platforms, where ARC is indeed used for objects. It does not apply on desktop platforms, where ARC is not used for objects. You need to make that distinction, it is very important. – Remy Lebeau Mar 01 '14 at 09:54
  • -1 for not specifying that reference counting is mobile only, and for misunderstanding the edited question regarding double frees. This lack of clarity caused confusion in subsequent questions... – magnus Mar 02 '14 at 02:31