Delphi "Free" TStringDynArray - delphi

Is there no need to free a TStringDynArray when created e.g. by SplitString? Attempts to free it fail as it is no object.
As I use it in a background process I am afraid that I create memory leaks by using it without explicitely freeing the memory.

No, a dynamic array is managed by the compiler. It is reference counted and will be freed when the reference count drops to zero.
(However, if the elements of the array are (pointers to) objects, these objects will not be freed automatically. Only the array itself is freed. In your case, the elements are strings, and they are also managed by the compiler.)
But you may occasionally want to free the memory before the variables go out of scope. For instance, if you have a global variable which is a huge dynamic array, you can explicitly do a SetLength(MyArray, 0) or MyArray := nil or Finalize(MyArray) to let go of it.

Related

Is a dynamic array automatically deallocated when length is decreased?

I alread know, that a dynamic array is automatically deallocated/freed after use.
Does the same applies for resizing, especially decreasing? The manual and most help sites only cover increasing the array size.
test: array of TLabel;
SetLength(test, 10);
// fill array here
SetLength(test, 2); // <=== are entries 3-10 are automatically destroyed?
are entries 3-10 are automatically destroyed?
No, they are not automatically destroyed because those entries are dynamically allocated (and are not managed types). Only the pointers that refer to those items are released. It is your responsibility to destroy the items if necessary, because the compiler has no way to guarantee you wouldn't still use them from another reference (or have already destroyed them).
I must also point out that technically items "3-10" is wrong. Dynamic array are zero based. So the references for entries 2 to 9 are the ones released.
I alread know, that a dynamic array is automatically deallocated/freed after use
In addition, your question indicates you don't properly understand this. It seems you believed that when your array goes out of scope the labels referenced would be automatically destroyed. This is incorrect!
No matter where how or why some/all dynamic array entries are released Delphi won't automatically destroy objects types or any dynamically allocated pointer memory. Delphi only automatically releases memory for primitives (Integer, TDateTime, Double short strings), records and managed types1 (interfaces, long strings, other dynamic arrays).
1 Of course this is via reference counting. I.e. reference is reduced by 1; and the underlying object/string/array is released if and only if refCount is reduced to zero.
As whosrdaddy pointed out, if you want automatic destruction of contained objects, then you need to use a container that implements an ownership concept. TObjectList is an example. Although it doesn't work exactly like a dynamic array, it's behaviour is similar enough that it can usually be used as a replacement very easily.

Why freeing not empty TList<Int64> does not cause memory leak?

Freeing not empty TList<Integer> does not cause memory leak because Integer is equal to pointer in size, and TList handles pointers perfectly. (This is as far as I understand it.) Freeing not empty TList<String> also does not cause memory leak, as String itself is a pointer and is carefully freed somewhere in Delphi's internals when it's no longer needed.
However, freeing any not empty TList<SomeClass> always produces memory leak, and it's understood why.
The thing I do not understand is why freeing not empty TList<Int64> does not produce memory leak.
Sorry for the noob question.
A TList<T> is simply a wrapper around a dynamic array of T. A dynamic array of T is a managed type and so does not need explicit destruction.
This leaves the elements of the array. Since Int64 is a value type, it needs no explicit destruction.
As a general rule, you need only destroy that which you created. You created the list, you need to destroy it. You did not create the elements themselves, so you do not need to destroy them.

Clear a TList or a TObjectList

I'm a bit puzzled of what to use for storing objects in a list.
Until now I have used TList and freed each item in a loop. Then I discovered TObjectList that do this automatically from Free. Then I saw this from the doc of TList.Clear:
Call Clear to empty the Items array and set the Count to 0. Clear also
frees the memory used to store the Items array and sets the Capacity to 0.
So it is basically the same. So
for TList
mylist.Clear;
myList.Free;
is the same as for TObjectList?
myList.Free;
Can TObjectList only be used for items as classes or can I store records?
1. TList won't free the elements, with both Clear or Free.
aList.Clear;
Will just set aList.Count := 0 without freeing the aList.Items[] elements. So you'll leak memory. You'll need an explicit free as such:
for i := 0 to aList.Count-1 do
TObject(aList[i]).Free;
But this is what TObjectList does... :)
About TObjectList, it is worth saying that TObjectList.Destroy is calling Clear.
So
aObjectList.Clear;
aObjectList.Free;
is exactly the same as
aObjectList.Free;
2. To store a list of records, you can use a dynamic array.
You'll get all TList methods (and more) with our dynamic array wrapper. That is, Add / Delete / Clear / Count / IndexOf / Find...
It has built-in serialization features (in binary or JSON), automated sorting and comparison (using RTTI) which are not existing with a TList/TObjectList. From Delphi 5 and later.
With more modern version of Delphi, you may use generics to handle the dynamic array, if you do not want to use a third-party library.
It's not the same TList.Clear only frees the memory allocated to store the pointers, not objects they are pointing to.
To avoid memory leaks you need to free the memory yourself - as you have been doing - or use TObjectList.
To answer the second question, TObjectList doesn't support storing records. You need to use TList (or something else) in that case.
Read what the documentation is saying more carefully:
Clear also frees the memory used to store the Items array
Only the memory for the array itself is freed, not the memory used by individual elements inside the array.
If you are using a recent version of Delphi I suggest that you use a generic list.
Right now you probably need to do a lot of casting when you use the objects from the list. With a generic list you don't have to do that anymore.
For instance if you have:
TMyObject = class(TObject);
Then you make the list like this:
TMyObjectList = TObjectList<TMyObject>;
There is an article in the Embarcadero Wiki:
http://docwiki.embarcadero.com/CodeExamples/XE8/en/Generics_Collections_TObjectList_(Delphi)
Really watching the TObjectList code in contnrs.pas OwnObjects does nothing. FOwnObjects is a variable declared in the private part of TObjectList that is not used anywhere in the TObjectList code (except just assigning the value), and there is no overridden Clear procedure in it.
So I'm afraid it necessary to free the memory just the same as in TList.

When exactly does a dynamic array get garbage collected?

Dynamic arrays are reference counted, and so the memory is freed automatically by the compiler. My question is, when exactly does this automatic freeing occur? Does it happen immediately, or at the end of the containing procedure?
Here is a concrete example
procedure DoStuff;
var data:TBytes;
begin
data:=GetData; // lets say data now contains 1 Gig of data.
DoStuffWithData(data);
// I now want to free up this 1Gig of memory before continuing.
// Is this call needed, or would the memory be freed in the next line anyway?
Finalize(data);
data:=GetMoreData; // The first array now has no remaining references
DoStuffWithData(data);
end
Is the call to Finalize() redundant?
The call to Finalize isn't quite redundant. It's true that the dynamic array's reference count will be decremented on the next line (therefore destroying the array, probably), but that will only happen after the new dynamic array is allocated. Just before the return of GetMoreData, but before the assignment takes place, there will be two dynamic arrays in memory. If you destroy the first one manually in advance, then you'll only have one array in memory at a time.
The second array that you store in data will get destroyed as DoStuff returns (assuming DoStuffWithData doesn't store a copy of the dynamic-array reference elsewhere, increasing its reference count).
When exactly does this automatic freeing occur? Does it happen immediately, or at the end of the containing procedure?
Dynamic memory associated with managed types (dynamic arrays fall into this class) is freed when the reference count is set to 0. This can happen at the following points:
The reference variable is assigned a new value. A call to Finalize can be thought of as the special case where the new values is nil.
The reference variable goes out of scope. For example:
The exit of a function is reached; local variables go out of scope.
An object is destroyed and its members go out of scope.
A pointer to a record is destroyed with the Dispose function; all fields of the record go out of scope.
A unit is finalized and all global variables defined in the unit are finalized.
Note that the various cases above only result in memory being freed when the reference that is being finalized or is leaving scope is the last remaining reference. In other words, when the reference count is 1.
In your specific example, assuming the Finalize is removed, you are creating a new dynamic array and assigning it to a variable that already holds a dynamic array. This then falls into the class described by item 1 in the list above. So in that sense the call to Finalize is superfluous.
Rob has explained the order in which the allocation and deallocation happens which is a good point. To the best of my knowledge that is an implementation detail that is not explicitly documented. However, I'd be astounded if that detail was ever changed.

What happens when increasing the size of a dynamic array?

I'd like to understand what happens when the size of a dynamic array is increased.
My understanding so far:
Existing array elements will remain unchanged.
New array elements are initialised to 0
All array elements are contiguous in memory.
When the array size is increased, will the extra memory be tacked onto the existing memory block, or will the existing elements be copied to a entirely new memory block?
Does changing the size of a dynamic array have consequences for pointers referencing existing array elements?
Thanks,
[edit] Incorrect assumption struck out. (New array elements are initialised to 0)
Existing array elements will remain unchanged: yes
New array elements are initialized to 0: yes (see update) no, unless it is an array of compiler managed types such as string, an other array or a variant
All array elements are contiguous in memory: yes
When the array size is increased, the array will be copied. From the doc:
...memory for a dynamic array is reallocated when you assign a value to the array or pass it to the SetLength procedure.
So yes, increasing the size of a dynamic array does have consequences for pointers referencing existing array elements.
If you want to keep references to existing elements, use their index in the array (0-based).
Update
Comments by Rob and David prompted me to check the initialization of dynamic arrays in Delphi5 (as I have that readily available anyway). First using some code to create various types of dynamic arrays and inspecting them in the debugger. They were all properly initialized, but that could still have been a result prior initialization of the memory location where they were allocated. So checked the RTL. It turns out D5 already has the FillChar statement in the DynArraySetLength method that Rob pointed to:
// Set the new memory to all zero bits
FillChar((PChar(p) + elSize * oldLength)^, elSize * (newLength - oldLength), 0);
In practice Embarcadero will always zero initialise new elements simply because to do otherwise would break so much code.
In fact it's a shame that they don't officially guarantee the zero allocation because it is so useful. The point is that often at the call site when writing SetLength you don't know whether you are growing or shrinking the array. But the implementation of SetLength does know – clearly it has to. So it really makes sense to have a well-defined action on any new elements.
What's more, if they want people to be able to switch easily between managed and native worlds then zero allocation is desirable since that's what fits with the managed code.

Resources