I've got a procedure that builds a somewhat big dynamic array of
objects. I've noticed that each time I call this procedure, the program
uses more and more memory, so I've come to the conclusion that I'm not
"cleaning up" after myself.
I would have thought that the array would be destroyed and memory freed
when the procedure exists. So could anyone enlighten me on how I should
free the memory that my array occupies. Do I need to run through all the
elements and setting it to free(?) or can it just SetLength(array, 0).
Anyone?
> free the memory that my array occupies. Do I need to run through all the
> elements and setting it to free(?)
yes
> I would have thought that the array would be destroyed and memory freed
> when the procedure exists.
It is, but what you store in the array are only pointers, since object
variables are (unlike records) only pointers to the data. The array
containing the pointers is automatically managed, but not the objects
to which these pointers point. So you'll have to free the single
objects one by one.
> So could anyone enlighten me on how I should
> free the memory that my array occupies. Do I need to run through all the
> elements and setting it to free(?) or can it just SetLength(array, 0).
You don't *set* something to free, you call the Free method of each
object. There is usually no need to use
SetLength(thearray, 0);
or
thearray := nil; // does the same as the above
and if you really want to make it nil, there's always FreeAndNil (well, not
really always; IIRC it was added in D3):
FreeAndNil(thearray);
> and if you really want to make it nil, there's always FreeAndNil (well,
not
> really always; IIRC it was added in D3):
> FreeAndNil(thearray);
FreeAndNil is only meant to be used for class instances, not for dynamic
arrays. Using FreeAndNil here could have catastrophic consequences. <g>
You're right. I guess you confused me by mentioning the Free method in the
other post. |-)
>and if you really want to make it nil, there's always FreeAndNil (well, not
>really always; IIRC it was added in D3):
(AFAIK in D5 ;-)
> FreeAndNil(thearray);
I don't recommend that with dynamic arrays - it should not work at all, because
no Free method exists for dynamic arrays. You are asking for trouble, when
Delphi tries to call an not-existing destructor for a non-object :-(
The FreeAndNil procedure is a dirty hack, which can only work when the argument
really is an object (of some class).
Setting an dynamic array (reference!) to nil can fail to erase the array, when
a reference to the same array exists somewhere else in the program. Remember
that dynamic arrays are reference counted!
IMO the only correct code for erasing an dynamic array is:
SetLength(thearray, 0);
DoDi
I just checked this on D4 and watched what was happening on MemProof
var
myArray, mySecondArray: array of integer;
SetLength(myArray,100000); // approx 400K allocated
myArray := nil; // approx 400K deallocated
OK .....that's as we'd expect... as is
SetLength(myArray,100000); // approx 400K allocated
mySecondArray := myArray;
myArray := nil; // no deallocation
AND..........
SetLength(myArray,100000); // approx 400K allocated
SetLength(myArray, 0); // approx 400K deallocated
so is this OK......... nice and efficient.......BUT
SetLength(myArray,100000); // approx 400K allocated
mySecondArray := myArray;
SetLength(myArray, 0); // no deallocation
the length change doesn't cause a deallocation while the reference count > 1
FURTHERMORE........
SetLength(myArray,100000); // approx 400K allocated
mySecondArray := myArray; // no change
SetLength(myArray,100000); // another approx 400K allocated
mySecondArray := myArray; // approx 400K deallocated
but what is this one all about?? Somewhere in help I seem to remember
reading SetLength guarantees a unique instance for strings. Perhaps that is
what is happening here, and when you make the second reference, the now
redundant second instance is deallocated.
Dave
>SetLength(myArray,100000); // approx 400K allocated
>mySecondArray := myArray; // no change
>SetLength(myArray,100000); // another approx 400K allocated
>mySecondArray := myArray; // approx 400K deallocated
>
I would think ...
SetLength(myArray,100000); // approx 400K allocated
mySecondArray := myArray; // no change
... because both reference the same array ...
SetLength(myArray,100000); // another approx 400K allocated
... SetLength guarantees a separate instance so a fresh allocation ...
mySecondArray := myArray; // approx 400K deallocated
... now these _are_ the same so both reference the same memory, and the
previous allocation for MySecondArray (actually the first allocation for
MyArray but transferred to MySecondArray on the second SetLength on MyArray) is
unnecessary.
SetLength guarantees a separate instance _only at that point in time_, not for
any future action. If you re-state the identity then that overcomes the
previous guarantee. Delphi is not yet capable of acting on what you may do in
the future <g>.
Alan Lloyd
alang...@aol.com
> SetLength(myArray,100000); // approx 400K allocated
refcount = 1
> mySecondArray := myArray;
refcount = 2
> SetLength(myArray, 0); // no deallocation
refcount decremented to 1 again
> the length change doesn't cause a deallocation while the reference count > 1
>
> FURTHERMORE........
>
> SetLength(myArray,100000); // approx 400K allocated
refcount = 1
> mySecondArray := myArray; // no change
refcount = 2
> SetLength(myArray,100000); // another approx 400K allocated
refcount of original array decremented to 1 again
new array allocated and assigned to myArray. recount also 1
> mySecondArray := myArray; // approx 400K deallocated
refcount of original array decremented to 0 -> freed
refcount of newer array incremented to 2
>SetLength(myArray,100000); // another approx 400K allocated
>
>... SetLength guarantees a separate instance so a fresh allocation ...
AFAIR it becomes tricky with "var" parameters. When a dynamic array is passed
as a "var" parameter to a subroutine, then the subroutine can modify and
redimension the array at will, and all operations act on the same array. This
is because then not an array reference is passed to the called subroutine, but
instead a reference to the variable which the caller supplied.
Dave wrote:
>mySecondArray := myArray;
>SetLength(myArray, 0); // no deallocation
>the length change doesn't cause a deallocation while the reference count > 1
This is correct. I think that I had in mind the above procedure, where I passed
an array as a "var" parameter, in which case the reference count of the array
is unchanged.
DoDi
Now that's a point I have never considered, yet I do this a lot....:-) I
guess an explicit assigment is necessary. If passing by var increased the
reference count, imagine what would happen if you latter sought to add stuff
to the original array and it spawned a new instance!!
Dave
Yes that makes sense
Dave