www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Understanding the GC

reply "Jeremy DeHaan" <dehaan.jeremiah gmail.com> writes:
I've been reading TDPL book and was also reading some posts on 
these forums about the GC, but I wanted to clarify a few things 
to make sure I am understanding correctly.

 From what I understand, when an object is recovered by the GC, 
the destructor may or may not be called. Why is that? Is it for 
performace reasons? What about the destructors of objects that 
the original object contained? Are they called when the item 
finally get's taken care of by the GC, or when the object is 
originally recovered by the GC?

My other question is, at the end of the program will GC go 
through every object it knows still holds memory and call their 
destructors? My guess is that it does so that the GC ensures all 
memory is successfully released before the program ends. I just 
want to make sure since in http://dlang.org/class.html, it says, 
"The garbage collector is not guaranteed to run the destructor 
for all unreferenced objects." What exactly is an unreferenced 
object?

Some other questions:

At what point is the GC called to do it's thing while the program 
is running? Is it at certain intervals or is it when the 
program's memory reaches a certain point?



Thanks very much for all the help!
Jan 29 2013
parent reply "Mike Parker" <aldacron gmail.com> writes:
On Wednesday, 30 January 2013 at 06:00:44 UTC, Jeremy DeHaan 
wrote:

 From what I understand, when an object is recovered by the GC, 
 the destructor may or may not be called. Why is that? Is it for
That's not quite correct. When the object is collected, its destructor will be called. But you have no control over when or if it will happen during runtime. More on this below in relation to your question about unreferenced objects.
 performace reasons? What about the destructors of objects that 
 the original object contained? Are they called when the item 
 finally get's taken care of by the GC, or when the object is 
 originally recovered by the GC?
Destructors of members will not be called when an object is collected. Only that of the object itself. But, there's no guarantee that any member references will still be valid when the object's destructor is called. See below for more.
 My other question is, at the end of the program will GC go 
 through every object it knows still holds memory and call their 
 destructors? My guess is that it does so that the GC ensures
Yes.
 all memory is successfully released before the program ends. I 
 just want to make sure since in http://dlang.org/class.html, it 
 says, "The garbage collector is not guaranteed to run the 
 destructor for all unreferenced objects." What exactly is an 
 unreferenced object?
A "referenced" object is one that is still in use by the program. Example: auto mc = new MyClass; // Here, there is exactly one reference to the MyClass instance // and it is still active. // But if i do this... auto mc2 = mc; // Now there are 2 references to the same instance. // Set one to null. mc = null; // Now there is only one reference. Null it... mc2 = null; // Now all references to the original instance are invalid, // so the object is unreachable by the program, so it is an // unreferenced object. The GC can only collect objects that it can be sure are unreferenced. Sometimes it can't be sure, in which case that particular object won't be collected during run time. For the objects that are determined to be collectable, there's no way to guarantee that any one of them will be collected in any given collection cycle. Because of that, you cannot rely on the order of destruction. Consider: class Foo {} class Bar { Foo f; } Bar b = new Bar; b.f = new Foo; Given that b.f is the only reference to this Foo instance, then this instance of Foo will become unreferenced at the same time that the instance of Bar does. But, again, we cannot rely on the Bar instance being collected and destructed before the Foo instance. So if you have a Bar destructor that tries to access a member in f, then you would get a crash some of the time. Worse, what if there were more references to the Foo instance outside of Bar that haven't been released yet? If you tried to destroy f from the Bar destructor, or perhaps call some sort of cleanup method to release system resources that f controls, you'll get a crash somewhere else in the program when another bit of code tries to access the no longer valid reference. That would likely be a marathon debugging session, because most of the time you don't expect that sort of thing.
 Some other questions:

 At what point is the GC called to do it's thing while the 
 program is running? Is it at certain intervals or is it when 
 the program's memory reaches a certain point?
My understanding is that the current implementation only runs collections when memory is allocated. Meaning, when you allocate a new object instance, or cause memory to be allocated via some built-in operations (on arrays, for example), the GC will check if anything needs to be collected and will do it at that time. I don't know if it's run on every allocation, or only when certain criteria or met, and I really don't care. That's an implementation detail. The D language itself does not specify any of that.
Jan 30 2013
next sibling parent "Mike Parker" <aldacron gmail.com> writes:
The take-home point of all of this is that you shouldn't rely on 
destructors for resource deallocation. You could do it in by 
manually destructing objects when you are finished with them (via 
the destroy() method), but then you have to be extra careful 
about class members, ownership, order of destruction, and all of 
that. I find that it's much simpler if I give each of my objects 
a specific cleanup method that releases its own resources. When 
an object is no longer needed, I call its cleanup method and 
clear any references to it. This ensures that system resources or 
whatever are released in a predictable order and I don't run into 
odd crashes from trying to release something that's no longer 
valid. The GC takes care of the rest.
Jan 30 2013
prev sibling next sibling parent reply "monarch_dodra" <monarchdodra gmail.com> writes:
On Wednesday, 30 January 2013 at 08:15:15 UTC, Mike Parker wrote:
 On Wednesday, 30 January 2013 at 06:00:44 UTC, Jeremy DeHaan 
 wrote:

 From what I understand, when an object is recovered by the GC, 
 the destructor may or may not be called. Why is that? Is it for
That's not quite correct. When the object is collected, its destructor will be called. But you have no control over when or if it will happen during runtime. More on this below in relation to your question about unreferenced objects.
To add to that, you also have to keep in mind that when the program terminates (even legally), instead of running a *full* collect cycle, the program just leaves, and lets the OS clear any allocated memory. This is both faster, and safer. What this means is that while there is a guarantee that "collection=>destruction", there is no guarantee that actual collection will happen. If you absolutely must be sure that something allocated gets *destroyed*, either destroy it yourself via an explicit call, or bind it to a stack based RAII scheme, possibly with reference counting.
 performace reasons? What about the destructors of objects that 
 the original object contained? Are they called when the item 
 finally get's taken care of by the GC, or when the object is 
 originally recovered by the GC?
Destructors of members will not be called when an object is collected. Only that of the object itself. But, there's no guarantee that any member references will still be valid when the object's destructor is called. See below for more.
Just to be clear, I suppose you (both) are talking about "member references"? EG: Nested classes? Destroying an object 100% guarantees its member destroyers are also called, outer to inner, first in first out, as part of the destruction process. The thing is that when you store a "class reference" then you call the destructor of the reference itself. References being glorified pointers, it basically means it does nothing. //---- struct S{} class A{} struct/class SA { S s; //destroying this means ~S gets called. A a; //"destroying" this means doing "a = null". }
Jan 30 2013
next sibling parent reply "Maxim Fomin" <maxim maxim-fomin.ru> writes:
On Wednesday, 30 January 2013 at 10:29:26 UTC, monarch_dodra 
wrote:
 On Wednesday, 30 January 2013 at 08:15:15 UTC, Mike Parker 
 wrote:
 Destructors of members will not be called when an object is 
 collected. Only that of the object itself. But, there's no 
 guarantee that any member references will still be valid when 
 the object's destructor is called. See below for more.
Just to be clear, I suppose you (both) are talking about "member references"? EG: Nested classes?
I found calling member class references as nested classes confusing - they are very different.
 Destroying an object 100% guarantees its member destroyers are 
 also called, outer to inner, first in first out, as part of the 
 destruction process. The thing is that when you store a "class 
 reference" then you call the destructor of the reference 
 itself. References being glorified pointers, it basically means 
 it does nothing.
Are you implying that in following code snippet: class A { ~this() { ... } } class B { A a; ~this() { ... } } destructor of A will be always called after B, so "a" member is always accessible in B's dtor?
Jan 30 2013
parent reply "monarch_dodra" <monarchdodra gmail.com> writes:
On Wednesday, 30 January 2013 at 11:57:01 UTC, Maxim Fomin wrote:
 On Wednesday, 30 January 2013 at 10:29:26 UTC, monarch_dodra 
 wrote:
 On Wednesday, 30 January 2013 at 08:15:15 UTC, Mike Parker 
 wrote:
 Destructors of members will not be called when an object is 
 collected. Only that of the object itself. But, there's no 
 guarantee that any member references will still be valid when 
 the object's destructor is called. See below for more.
Just to be clear, I suppose you (both) are talking about "member references"? EG: Nested classes?
I found calling member class references as nested classes confusing - they are very different.
 Destroying an object 100% guarantees its member destroyers are 
 also called, outer to inner, first in first out, as part of 
 the destruction process. The thing is that when you store a 
 "class reference" then you call the destructor of the 
 reference itself. References being glorified pointers, it 
 basically means it does nothing.
Are you implying that in following code snippet: class A { ~this() { ... } } class B { A a; ~this() { ... } } destructor of A will be always called after B, so "a" member is always accessible in B's dtor?
No. What gave you that idea? I said the "reference" a will be destroyed (set to null). The actual referenced "class instance" may or may not be destroyed by then. The class instance of A itself is not a member of B.
Jan 30 2013
parent reply "Maxim Fomin" <maxim maxim-fomin.ru> writes:
On Wednesday, 30 January 2013 at 12:08:07 UTC, monarch_dodra 
wrote:
 On Wednesday, 30 January 2013 at 11:57:01 UTC, Maxim Fomin 
 wrote:
 On Wednesday, 30 January 2013 at 10:29:26 UTC, monarch_dodra 
 wrote:
 On Wednesday, 30 January 2013 at 08:15:15 UTC, Mike Parker 
 wrote:
 Destructors of members will not be called when an object is 
 collected. Only that of the object itself. But, there's no 
 guarantee that any member references will still be valid 
 when the object's destructor is called. See below for more.
Just to be clear, I suppose you (both) are talking about "member references"? EG: Nested classes?
I found calling member class references as nested classes confusing - they are very different.
 Destroying an object 100% guarantees its member destroyers 
 are also called, outer to inner, first in first out, as part 
 of the destruction process. The thing is that when you store 
 a "class reference" then you call the destructor of the 
 reference itself. References being glorified pointers, it 
 basically means it does nothing.
Are you implying that in following code snippet: class A { ~this() { ... } } class B { A a; ~this() { ... } } destructor of A will be always called after B, so "a" member is always accessible in B's dtor?
No. What gave you that idea? I said the "reference" a will be destroyed (set to null). The actual referenced "class instance" may or may not be destroyed by then. The class instance of A itself is not a member of B.
English is not native for me. Sometimes non-natives misunderstand the meaning of the words.
Jan 30 2013
parent "monarch_dodra" <monarchdodra gmail.com> writes:
On Wednesday, 30 January 2013 at 12:17:33 UTC, Maxim Fomin wrote:
 English is not native for me. Sometimes non-natives 
 misunderstand the meaning of the words.
My apologies.
Jan 30 2013
prev sibling parent reply "Jeremy DeHaan" <dehaan.jeremiah gmail.com> writes:
On Wednesday, 30 January 2013 at 10:29:26 UTC, monarch_dodra 
wrote:
 To add to that, you also have to keep in mind that when the 
 program terminates (even legally), instead of running a *full* 
 collect cycle, the program just leaves, and lets the OS clear 
 any allocated memory. This is both faster, and safer.

 What this means is that while there is a guarantee that 
 "collection=>destruction", there is no guarantee that actual 
 collection will happen.

 If you absolutely must be sure that something allocated gets 
 *destroyed*, either destroy it yourself via an explicit call, 
 or bind it to a stack based RAII scheme, possibly with 
 reference counting.
So there is no guarantee at all that a destructor will be called even at the end of the program? Because there is an example in the book using a class destructor to free allocated data. I definitely understand now about how not to rely on a destructor to free up memory during runtime, but it seems counterintuitive to have the ability to write a destructor with no guarantee it would ever be called even at cleanup.
Jan 31 2013
parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Thu, 31 Jan 2013 18:27:59 -0500, Jeremy DeHaan  
<dehaan.jeremiah gmail.com> wrote:

 On Wednesday, 30 January 2013 at 10:29:26 UTC, monarch_dodra wrote:
 To add to that, you also have to keep in mind that when the program  
 terminates (even legally), instead of running a *full* collect cycle,  
 the program just leaves, and lets the OS clear any allocated memory.  
 This is both faster, and safer.

 What this means is that while there is a guarantee that  
 "collection=>destruction", there is no guarantee that actual collection  
 will happen.

 If you absolutely must be sure that something allocated gets  
 *destroyed*, either destroy it yourself via an explicit call, or bind  
 it to a stack based RAII scheme, possibly with reference counting.
So there is no guarantee at all that a destructor will be called even at the end of the program? Because there is an example in the book using a class destructor to free allocated data.
I'm pretty sure all GCs do not have a guarantee of running all destructors. I think D's GC makes a good effort to do so.
 I definitely understand now about how not to rely on a destructor to  
 free up memory during runtime, but it seems counterintuitive  to have  
 the ability to write a destructor with no guarantee it would ever be  
 called even at cleanup.
A destructor should ONLY be used to free up resources other than GC allocated memory. Because of that, it's generally not used. It should be used almost as a "last resort". For example, a class that holds a file descriptor should have both a destructor (which closes the descriptor) and a manual close method. The former is to clean up the file descriptor in case nobody thought to close it manually before all references were gone, and the latter is because file descriptors are not really managed by the GC, and so should be cleaned up when they are no longer used. This kind of gives us a paradox, since the class is managed via the GC, how do you know it's no longer used (that is, how do you know this is the last reference to it)? That is really up to the application design. But I wouldn't recommend relying on the GC to clean up your descriptors. -Steve
Jan 31 2013
next sibling parent "Jeremy DeHaan" <dehaan.jeremiah gmail.com> writes:
On Thursday, 31 January 2013 at 23:53:26 UTC, Steven 
Schveighoffer wrote:
 On Thu, 31 Jan 2013 18:27:59 -0500, Jeremy DeHaan 
 <dehaan.jeremiah gmail.com> wrote:

 On Wednesday, 30 January 2013 at 10:29:26 UTC, monarch_dodra 
 wrote:
 To add to that, you also have to keep in mind that when the 
 program terminates (even legally), instead of running a 
 *full* collect cycle, the program just leaves, and lets the 
 OS clear any allocated memory. This is both faster, and safer.

 What this means is that while there is a guarantee that 
 "collection=>destruction", there is no guarantee that actual 
 collection will happen.

 If you absolutely must be sure that something allocated gets 
 *destroyed*, either destroy it yourself via an explicit call, 
 or bind it to a stack based RAII scheme, possibly with 
 reference counting.
So there is no guarantee at all that a destructor will be called even at the end of the program? Because there is an example in the book using a class destructor to free allocated data.
I'm pretty sure all GCs do not have a guarantee of running all destructors. I think D's GC makes a good effort to do so.
 I definitely understand now about how not to rely on a 
 destructor to free up memory during runtime, but it seems 
 counterintuitive  to have the ability to write a destructor 
 with no guarantee it would ever be called even at cleanup.
A destructor should ONLY be used to free up resources other than GC allocated memory. Because of that, it's generally not used. It should be used almost as a "last resort". For example, a class that holds a file descriptor should have both a destructor (which closes the descriptor) and a manual close method. The former is to clean up the file descriptor in case nobody thought to close it manually before all references were gone, and the latter is because file descriptors are not really managed by the GC, and so should be cleaned up when they are no longer used. This kind of gives us a paradox, since the class is managed via the GC, how do you know it's no longer used (that is, how do you know this is the last reference to it)? That is really up to the application design. But I wouldn't recommend relying on the GC to clean up your descriptors. -Steve
This makes a whole lot of sense to me. I never realized that a GC could be imperfect in this regard. I also don't know much about GC design and implementation, but that was the purpose of this thread! I definitely like the idea of having a manual way of cleaning up and a destructor as a back up for times such as these. Thanks for all the help guys! I learned a lot!
Jan 31 2013
prev sibling parent reply "monarch_dodra" <monarchdodra gmail.com> writes:
On Thursday, 31 January 2013 at 23:53:26 UTC, Steven 
Schveighoffer wrote:
 A destructor should ONLY be used to free up resources other 
 than GC allocated memory.  Because of that, it's generally not 
 used.

 It should be used almost as a "last resort".

 For example, a class that holds a file descriptor should have 
 both a destructor (which closes the descriptor) and a manual 
 close method.  The former is to clean up the file descriptor in 
 case nobody thought to close it manually before all references 
 were gone, and the latter is because file descriptors are not 
 really managed by the GC, and so should be cleaned up when they 
 are no longer used.

 This kind of gives us a paradox, since the class is managed via 
 the GC, how do you know it's no longer used (that is, how do 
 you know this is the last reference to it)?  That is really up 
 to the application design.  But I wouldn't recommend relying on 
 the GC to clean up your descriptors.

 -Steve
I've actually run into this very issue: I was iterating on files, opening them, and placing the descriptor in GC-allocated RAII data. I can't remember if class or struct, but not a big issue. Come to think about it, I think I was using "File", but allocating them because I thought they were classes `auto f = new File("my file", "r")`. After running for a second, my program halts, because an exception was thrown trying to open a new file: "Cannot open file: Too many open file handles". It was basically: Sure, the GC will destroy and close files for you... if you forget... eventually... I ended up closing them in scope(exit) blocks. Problem immediately solved. Or I could have stopped allocating my File's on the heap. Either way, it shows you shouldn't rely on the GC for deterministic destruction.
Jan 31 2013
next sibling parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Fri, 01 Feb 2013 02:07:00 -0500, monarch_dodra <monarchdodra gmail.com>  
wrote:

 On Thursday, 31 January 2013 at 23:53:26 UTC, Steven Schveighoffer wrote:
 A destructor should ONLY be used to free up resources other than GC  
 allocated memory.  Because of that, it's generally not used.

 It should be used almost as a "last resort".

 For example, a class that holds a file descriptor should have both a  
 destructor (which closes the descriptor) and a manual close method.   
 The former is to clean up the file descriptor in case nobody thought to  
 close it manually before all references were gone, and the latter is  
 because file descriptors are not really managed by the GC, and so  
 should be cleaned up when they are no longer used.

 This kind of gives us a paradox, since the class is managed via the GC,  
 how do you know it's no longer used (that is, how do you know this is  
 the last reference to it)?  That is really up to the application  
 design.  But I wouldn't recommend relying on the GC to clean up your  
 descriptors.

 -Steve
I've actually run into this very issue: I was iterating on files, opening them, and placing the descriptor in GC-allocated RAII data. I can't remember if class or struct, but not a big issue. Come to think about it, I think I was using "File", but allocating them because I thought they were classes `auto f = new File("my file", "r")`. After running for a second, my program halts, because an exception was thrown trying to open a new file: "Cannot open file: Too many open file handles". It was basically: Sure, the GC will destroy and close files for you... if you forget... eventually... I ended up closing them in scope(exit) blocks. Problem immediately solved. Or I could have stopped allocating my File's on the heap. Either way, it shows you shouldn't rely on the GC for deterministic destruction.
Actually, that's a different problem. File is a struct, and structs do NOT have their destructor run by the GC. Only Objects do. This is a GC limitation, since structs do not contain a pointer to their typeinfo like classes do, and there is no provision for storing a pointer to the typeinfo of a block. It could be fixed by a more precise GC. AIUI, we have something like that coming, but I've been hearing that for more than a year ;) -Steve
Feb 01 2013
next sibling parent reply "monarch_dodra" <monarchdodra gmail.com> writes:
On Friday, 1 February 2013 at 15:37:25 UTC, Steven Schveighoffer 
wrote:
 On Fri, 01 Feb 2013 02:07:00 -0500, monarch_dodra 
 <monarchdodra gmail.com> wrote:

 On Thursday, 31 January 2013 at 23:53:26 UTC, Steven 
 Schveighoffer wrote:
 A destructor should ONLY be used to free up resources other 
 than GC allocated memory.  Because of that, it's generally 
 not used.

 It should be used almost as a "last resort".

 For example, a class that holds a file descriptor should have 
 both a destructor (which closes the descriptor) and a manual 
 close method.  The former is to clean up the file descriptor 
 in case nobody thought to close it manually before all 
 references were gone, and the latter is because file 
 descriptors are not really managed by the GC, and so should 
 be cleaned up when they are no longer used.

 This kind of gives us a paradox, since the class is managed 
 via the GC, how do you know it's no longer used (that is, how 
 do you know this is the last reference to it)?  That is 
 really up to the application design.  But I wouldn't 
 recommend relying on the GC to clean up your descriptors.

 -Steve
I've actually run into this very issue: I was iterating on files, opening them, and placing the descriptor in GC-allocated RAII data. I can't remember if class or struct, but not a big issue. Come to think about it, I think I was using "File", but allocating them because I thought they were classes `auto f = new File("my file", "r")`. After running for a second, my program halts, because an exception was thrown trying to open a new file: "Cannot open file: Too many open file handles". It was basically: Sure, the GC will destroy and close files for you... if you forget... eventually... I ended up closing them in scope(exit) blocks. Problem immediately solved. Or I could have stopped allocating my File's on the heap. Either way, it shows you shouldn't rely on the GC for deterministic destruction.
Actually, that's a different problem. File is a struct, and structs do NOT have their destructor run by the GC. Only Objects do. This is a GC limitation, since structs do not contain a pointer to their typeinfo like classes do, and there is no provision for storing a pointer to the typeinfo of a block. It could be fixed by a more precise GC. AIUI, we have something like that coming, but I've been hearing that for more than a year ;) -Steve
Oh. Wow. That's news to me. I'd say as long as you don't new your structs, you are fine, but apparently, dynamic arrays don't clean up after themselves either (!) That's a whole other ballgame... That's quite scary. It brings back into question a few of my implementations...
Feb 01 2013
parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Fri, 01 Feb 2013 10:46:11 -0500, monarch_dodra <monarchdodra gmail.com>  
wrote:

 On Friday, 1 February 2013 at 15:37:25 UTC, Steven Schveighoffer wrote:
 This is a GC limitation, since structs do not contain a pointer to  
 their typeinfo like classes do, and there is no provision for storing a  
 pointer to the typeinfo of a block.  It could be fixed by a more  
 precise GC.  AIUI, we have something like that coming, but I've been  
 hearing that for more than a year ;)

 -Steve
Oh. Wow. That's news to me. I'd say as long as you don't new your structs, you are fine, but apparently, dynamic arrays don't clean up after themselves either (!) That's a whole other ballgame...
Dynamic arrays do clean up the array memory. For arrays of objects, or arrays of structs that have only GC-based resources, those items are also individually cleaned up by the GC. But if you had, for instance, an array of self-closing file descriptor structs, those would NOT be cleaned up by the GC.
 That's quite scary. It brings back into question a few of my  
 implementations...
People should definitely be aware of this limitation. It usually is not of any concern because it is rare to have a struct that has a non-GC reference on the heap. But it is something to be aware of. Note that if your struct is a *member* of a class, it's destructor will be called because it's destructor is called as part of the object's destructor. But this isn't always what you want! I think people have been harping on this limitation of File (and reference-counted structs in general) for a long time. There is a bugzilla issue for it somewhere. -Steve
Feb 01 2013
prev sibling parent FG <home fgda.pl> writes:
On 2013-02-01 16:37, Steven Schveighoffer wrote:
 Actually, that's a different problem.  File is a struct, and structs do NOT
have
 their destructor run by the GC.  Only Objects do.

 This is a GC limitation, since structs do not contain a pointer to their
 typeinfo like classes do, and there is no provision for storing a pointer to
the
 typeinfo of a block.  It could be fixed by a more precise GC.  AIUI, we have
 something like that coming, but I've been hearing that for more than a year ;)
So currently the only way to make a struct's destructor work when the struct is on the heap is to encapsulate that struct in a class? I have tested the following: struct A { ... } class C { A a; ... } A a = A(); // OK A *b = new A(); // BAD, no finalization C c = new C(); // OK, a's destructor will be called
Feb 01 2013
prev sibling parent Sean Kelly <sean invisibleduck.org> writes:
On Jan 31, 2013, at 11:07 PM, "monarch_dodra" <monarchdodra gmail.com> wrote=
:

 On Thursday, 31 January 2013 at 23:53:26 UTC, Steven Schveighoffer wrote:
=20
 A destructor should ONLY be used to free up resources other than GC alloc=
ated memory. Because of that, it's generally not used.
=20
 It should be used almost as a "last resort".
=20
 For example, a class that holds a file descriptor should have both a dest=
ructor (which closes the descriptor) and a manual close method. The former i= s to clean up the file descriptor in case nobody thought to close it manuall= y before all references were gone, and the latter is because file descriptor= s are not really managed by the GC, and so should be cleaned up when they ar= e no longer used.
=20
 This kind of gives us a paradox, since the class is managed via the GC, h=
ow do you know it's no longer used (that is, how do you know this is the las= t reference to it)? That is really up to the application design. But I wou= ldn't recommend relying on the GC to clean up your descriptors.
=20
 -Steve
=20 I've actually run into this very issue: I was iterating on files, opening t=
hem, and placing the descriptor in GC-allocated RAII data. I can't remember i= f class or struct, but not a big issue. Come to think about it, I think I wa= s using "File", but allocating them because I thought they were classes `aut= o f =3D new File("my file", "r")`.
=20
 After running for a second, my program halts, because an exception was thr=
own trying to open a new file:
 "Cannot open file: Too many open file handles".
=20
 It was basically: Sure, the GC will destroy and close files for you... if y=
ou forget... eventually...
=20
 I ended up closing them in scope(exit) blocks. Problem immediately solved.=
Or I could have stopped allocating my File's on the heap.
=20
 Either way, it shows you shouldn't rely on the GC for deterministic destru=
ction. The GC currently doesn't finalize structs, only classes. So that's an issue a= s well.=20=
Feb 01 2013
prev sibling parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Wed, 30 Jan 2013 03:15:14 -0500, Mike Parker <aldacron gmail.com> wrote:

 My understanding is that the current implementation only runs  
 collections when memory is allocated. Meaning, when you allocate a new  
 object instance, or cause memory to be allocated via some built-in  
 operations (on arrays, for example), the GC will check if anything needs  
 to be collected and will do it at that time. I don't know if it's run on  
 every allocation, or only when certain criteria or met, and I really  
 don't care. That's an implementation detail. The D language itself does  
 not specify any of that.
This isn't quite accurate. The GC first checks to see if there is a free block that would satisfy the allocation, and if it can't find one, THEN it runs a collection cycle, and if then it cannot allocate the block from any memory regained, it then asks for more memory from the OS. This can lead to the collection cycle running quite a bit when allocating lots of data. I don't know if there are any measures to mitigate that, but there probably should be. -Steve
Jan 31 2013
parent Sean Kelly <sean invisibleduck.org> writes:
GG.reserve can be handy for this. It tells the GC to pre allocate a block of=
 memory from the OS.=20

On Jan 31, 2013, at 7:12 AM, "Steven Schveighoffer" <schveiguy yahoo.com> wr=
ote:

 On Wed, 30 Jan 2013 03:15:14 -0500, Mike Parker <aldacron gmail.com> wrote=
:
=20
 My understanding is that the current implementation only runs collections=
when memory is allocated. Meaning, when you allocate a new object instance,= or cause memory to be allocated via some built-in operations (on arrays, fo= r example), the GC will check if anything needs to be collected and will do i= t at that time. I don't know if it's run on every allocation, or only when c= ertain criteria or met, and I really don't care. That's an implementation de= tail. The D language itself does not specify any of that.
=20
 This isn't quite accurate.
=20
 The GC first checks to see if there is a free block that would satisfy the=
allocation, and if it can't find one, THEN it runs a collection cycle, and i= f then it cannot allocate the block from any memory regained, it then asks f= or more memory from the OS.
=20
 This can lead to the collection cycle running quite a bit when allocating l=
ots of data. I don't know if there are any measures to mitigate that, but t= here probably should be.
=20
 -Steve
Jan 31 2013