www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Bug? GC collects memory that references itself.

reply Jeremie Pelletier <jeremiep gmail.com> writes:
I need objects that may live without any references in GC memory, this 
is for bindings to a C++ library. Using ranges or roots would be very 
inneficient so my solution was to have the objects reference themselves 
until the C++ side of the object calls a finalizer which nullify the 
reference to let the GC collect the memory.

The GC doesn't see things this way however, and collects the objects 
even when they reference themselves.

I've made a simple test program, the objects should never get collected.

---

import core.memory;
import std.stdio;

class Foo {
	Foo self;
	this() { self = this; }
	~this() { assert(!self); }
}

void main() {
	foreach(i; 0 .. 50) new Foo;
	GC.collect();
	writeln("No object collected!");
}

---

If its a feature of the GC to prevent objects from never being 
collected, how do I bypass it?

Jeremie
Oct 26 2009
next sibling parent Rainer Deyke <rainerd eldwood.com> writes:
Jeremie Pelletier wrote:
 I need objects that may live without any references in GC memory, this
 is for bindings to a C++ library. Using ranges or roots would be very
 inneficient so my solution was to have the objects reference themselves
 until the C++ side of the object calls a finalizer which nullify the
 reference to let the GC collect the memory.
That's a (degenerate) reference cycle. Collection of reference cycles is not only intentional, but one of the main reasons for using GC in the first place. So, don't do that. It doesn't work, it will never work, and fundamentally it /can't/ work and /shouldn't/ work. -- Rainer Deyke - rainerd eldwood.com
Oct 26 2009
prev sibling next sibling parent reply "Simen Kjaeraas" <simen.kjaras gmail.com> writes:
Jeremie Pelletier <jeremiep gmail.com> wrote:

 I need objects that may live without any references in GC memory, this  
 is for bindings to a C++ library. Using ranges or roots would be very  
 inneficient so my solution was to have the objects reference themselves  
 until the C++ side of the object calls a finalizer which nullify the  
 reference to let the GC collect the memory.

 The GC doesn't see things this way however, and collects the objects  
 even when they reference themselves.

 I've made a simple test program, the objects should never get collected.

 ---

 import core.memory;
 import std.stdio;

 class Foo {
 	Foo self;
 	this() { self = this; }
 	~this() { assert(!self); }
 }

 void main() {
 	foreach(i; 0 .. 50) new Foo;
 	GC.collect();
 	writeln("No object collected!");
 }

 ---

 If its a feature of the GC to prevent objects from never being  
 collected, how do I bypass it?
As Rayner pointed out, that's a cycle, and something the GC should ignore. As for how to make it work: class Foo { new( uint size ) { return malloc( size ); // Use good old malloc to allocate memory } delete( void* p ) { free( p ); // And free it with good old free. } } This is described under http://digitalmars.com/d/1.0/class.html#allocators You might also need to use gc_addRoot to register the class with the GC, and gc_removeRoot to unregister it upon deletion. -- Simen
Oct 27 2009
parent reply Jeremie Pelletier <jeremiep gmail.com> writes:
Simen Kjaeraas wrote:
 Jeremie Pelletier <jeremiep gmail.com> wrote:
 
 I need objects that may live without any references in GC memory, this 
 is for bindings to a C++ library. Using ranges or roots would be very 
 inneficient so my solution was to have the objects reference 
 themselves until the C++ side of the object calls a finalizer which 
 nullify the reference to let the GC collect the memory.

 The GC doesn't see things this way however, and collects the objects 
 even when they reference themselves.

 I've made a simple test program, the objects should never get collected.

 ---

 import core.memory;
 import std.stdio;

 class Foo {
     Foo self;
     this() { self = this; }
     ~this() { assert(!self); }
 }

 void main() {
     foreach(i; 0 .. 50) new Foo;
     GC.collect();
     writeln("No object collected!");
 }

 ---

 If its a feature of the GC to prevent objects from never being 
 collected, how do I bypass it?
As Rayner pointed out, that's a cycle, and something the GC should ignore. As for how to make it work: class Foo { new( uint size ) { return malloc( size ); // Use good old malloc to allocate memory } delete( void* p ) { free( p ); // And free it with good old free. } } This is described under http://digitalmars.com/d/1.0/class.html#allocators You might also need to use gc_addRoot to register the class with the GC, and gc_removeRoot to unregister it upon deletion. -- Simen
It would be too slow for the GC to manage hundreds or thousands of roots. I think I'm just going to use an array of pointers and let the objects know their index in that array, it seems to be the fastest solution with the GC. The D class tree for the C++ interface can be subclassed so they will most likely contain references to D memory. Thanks all for your suggestions and help! Jeremie
Oct 27 2009
parent Stewart Gordon <smjg_1998 yahoo.com> writes:
Jeremie Pelletier wrote:
 Simen Kjaeraas wrote:
 Jeremie Pelletier <jeremiep gmail.com> wrote:

 I need objects that may live without any references in GC memory, 
 this is for bindings to a C++ library. Using ranges or roots would be 
 very inneficient so my solution was to have the objects reference 
 themselves until the C++ side of the object calls a finalizer which 
 nullify the reference to let the GC collect the memory.

 The GC doesn't see things this way however, and collects the objects 
 even when they reference themselves.
Correct. For a GC-allocated object to remain alive, it must be reachable, i.e. there must exist a chain of references that leads to it from at least one of: - each thread's stack - the static data segment - the list of roots added using std.gc.addRoot or std.gc.addRange. The existence of some reference of it somewhere in the heap is not sufficient. If it were, the GC would be very slow, since it would have to either - scan every bit of heap-allocated memory, not just what is reachable - use reference counting, itself a performance hit <snip>
 It would be too slow for the GC to manage hundreds or thousands of roots.
Really? Have you experimented?
 I think I'm just going to use an array of pointers and let the objects 
 know their index in that array, it seems to be the fastest solution with 
 the GC.
Certainly one way to do it, though I'm a little confused about why adding GC roots would be any slower than this.
 The D class tree for the C++ interface can be subclassed so they 
 will most likely contain references to D memory.
<snip> If that's the case, there will likely be a number of GC nodes for each of your special objects, and so an extra one surely isn't going to add up to much. Stewart.
Oct 29 2009
prev sibling parent Kagamin <spam here.lot> writes:
Jeremie Pelletier Wrote:

 If its a feature of the GC to prevent objects from never being 
 collected, how do I bypass it?
Implement IUnknown?
Oct 27 2009