Wrestling the Garbage Collector.. And loosing..
Steven Schveighoffer
schveiguy at yahoo.com
Wed Apr 30 11:13:03 PDT 2008
"Simen Haugen" wrote
> Hi.
>
> I'm having a problem with my application using extremly much memory, and
> the only way I'm able to free it is by deleting objects explicitly. I
> guess I'm not understanding the GC right, but I thought that it should
> free all objects without references to it when it's collecting, but my
> testing below shows I'm doing something wrong.
>
> // Test 1
> auto str = new char[512];
> delete str;
> str = new char[512]; // OK. No new allocation
>
> // Test 2
> auto str = new char[512];
> str = new char[512]; // New memory allocated. GC isn't run. Guess it's
> expected behavior.
>
> // Test 3
> auto str = new char[512];
> str = null;
> str = new char[512]; // New memory allocated. GC isn't run. Guess it's
> expected behavior.
>
> // Test 4
> auto str = new char[512];
> str = null;
> GC.collect;
> str = new char[512]; // New memory allocated. Why?
>
> // Test 5
> auto str = new char[512];
> str.length = 0;
> GC.collect;
> str = new char[512]; // New memory allocated. Why?
>
> I would have thought Test 4 and 5 shouldn't allocate more memory either as
> I remove the reference (or zero length) and run the GC, but it does.
> I would also think Test 2 and 3 should make the GC reclaim memory when it
> is run.
The GC is based on pointers. So if a thread stack has pointers to any GC
data, it will not collect that data.
In test 5, the str array has length 0, but the array ptr is still a pointer
pointing to the beginning of that allocated data. I would not expect Test 5
to collect the original data.
In Test 4, this one may be due to the pointer still being in the registers.
You might want to try executing some other functions or statements before
calling GC.collect, but even then, it might not clear out the particular
register that held the pointer.
There are other causes to the GC not reclaiming memory. The GC only
allocates memory based on bin sizes, in 16, 32, ..., 4096+ sizes. So if you
allocate just over one of those sizes, then you are still allocating the
whole bin size. Do this alot, and you will see a huge waste of memory.
This is a tradeoff of minimal memory size vs. efficiency to find an empty
memory location.
Another cause of lost memory is appending to an array. The GC never
releases pages back to the OS (this might have been fixed in Tango). If you
append a byte to an array over and over again, for instance, what happens?
// array.length == 16
array ~= 'a';
This needs to move array into another bin, but what happens to the original
bin? It's marked as free, but the memory is still held by the GC for the
next <=16 byte allocation.
What ends up happening is as you append more and more data, the number of
pages allocated grows a lot. Even though this memory is marked as 'free',
it's still being used by your application.
-Steve
More information about the Digitalmars-d-learn
mailing list