When exactly does a dynamic array get garbage collected? - delphi

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.

Related

Delphi "Free" TStringDynArray

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.

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.

VB6 Collections/Object References

I was wondering if someone could tell what happens with regards to memory when the following happens:
Dict = New Dictionary --- Col = New Collection
Dict.Add Key, CustomClassOne
Dict.Add Key2, CustomClassTwo
Dict.Add Key3, CustomClassThree
Dict.Remove Key3
At this point is Key3 removed from memory or would I have to Set Dict.Item(Key3) = Nothing to remove it from memory?
Set Dict = Nothing '// will this remove All the above added custom class objects?
Set Col = Nothing '// Same question as above
Ugh VB memory management.... TY for your time,
- Austin
VB is reference counted.
The rules of when an object is released from memory is simple.. it happens when there are no more references to that object. Each time an object goes out of scope (such as the end of a function) its reference count is decreased; which may in turn cause any objects which were referenced by this object to have their reference counts decreases too; and if their reference counts get to 0, they too are released from memory.
This is why there is usually no need to set an object's reference to Nothing... that will decrease its reference count, but that will also happen when it goes out of scope.
So to answer your question:
Dict.Remove Key3 is all that is required to remove CustomClassThree and Key3 from memory (as long as you don't have other references pointing to this object).
Set Dict = Nothing will remove everything from memory, but this would happen anyway when it goes out of scope (again assuming there are no other references pointing to the objects it contains).
Col doesn't seem to have much to do with the other statements and would be removed from memory when it goes out of scope without needing to set Col = nothing
Note:
The purpose of setting a reference to nothing is only really useful if you have objects which both have references to each other. Look up circular references for the details
With both Scripting.Dictionary and Collection instances when the last reference to the object is gone then the object references they hold are released. Whether or not the objects themselves are deallocated depends on whether or not another variable holds a reference to the same object.
Think of each reference as a rope holding a rock above an abyss. Until the last rope is cut the rock doesn't drop out of existence.
Removing an item from a Dictionary or Collection cuts that one rope.

Understanding memory allocation for TList<RecordType>

I have to store a TList of something that can easily be implemented as a record in Delphi (five simple fields). However, it's not clear to me what happens when I do TList<TMyRecordType>.Add(R).
Since R is a local variable in the procedure in which I create the my TList, I assume that the memory for it will be released when the function returns. Does this leave an invalid record pointer in the list? Or does the list know to copy-on-assign? If the former, I assume I would have to manually manager the memory for R with New() and Dispose(), is that correct?
Alternatively, I can "promote" my record type to a class type by simply declaring the fields public (without even bothering with making them formal properties). Is that considered OK, or ought I to take the time to build out the class with private fields and public properties?
Simplified: records are blobs of data and are passed around by value - i.e. by copying them - by default. TList<T> stores values in an array of type T. So, TList<TMyRecordType>.Add(R) will copy the value R into the array at position Count, and increment the Count by one. No need to worry about allocation or deallocation of memory.
More complex issues that you usually don't need to worry about: if your record contains fields of a string type, an interface type, a dynamic array, or a record which itself contains fields of one of these types, then it's not just a simply copy of data; instead, CopyRecord from System.pas is used, which ensures that reference counts are updated correctly. But usually you don't need to worry about this detail unless you are using Move to shift the bits around yourself, or doing similar low-level operations.

Do objects also have some scope like methods local variables have?

i have a confusion how gabage collector decides that an object is no more in use, do object have some scope?
like if i have code
class A { in x; m1(){}}
class B {A a=new a(); a.x=10; }
so i want to know that when the object become unusable
i mean in the above code if class be reaches to end line then when it exit that class do object a can go for garbage collection, and after that A class varibale will agian hold is default value of will permanently value 10
Only declarations (type, member, local) have scope. Nothing else.
Garbage collectors work by finding and tagging all objects that are reachable from known starting points (e.g., each thread's stack, all static variables, ...), and then blowing away objects that didn't get tagged. The full explanation is usually far more complex, but this is the essence of it.
Objects do have scope as any other variable and as defined by language rules.
An object is garbage collected when there are no other objects referencing it.
GC has its more or less complex algorithm for determining that . One of them is reference counting.
When a local variabla goes out of scope it looses a reference, if refernece coutn is 0 then is garbage collected.
Garbage collection is not deterministic, that is you can't decide eactly when to garbage collect it.
Setting a variable to null will basically make the variable garbage collectable.

Resources