digitalmars.D - Manual Deletion from Destructor
- dsimcha <dsimcha yahoo.com> Mar 14 2009
- Chad J <gamerchad __spam.is.bad__gmail.com> Mar 14 2009
- Jarrett Billingsley <jarrett.billingsley gmail.com> Mar 14 2009
- Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> Mar 14 2009
- dsimcha <dsimcha yahoo.com> Mar 14 2009
- Sean Kelly <sean invisibleduck.org> Mar 14 2009
- Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> Mar 14 2009
- dsimcha <dsimcha yahoo.com> Mar 14 2009
- BCS <none anon.com> Mar 14 2009
- Frits van Bommel <fvbommel REMwOVExCAPSs.nl> Mar 14 2009
I sometimes run into false pointer issues when using very large data
structures in D. Often, these data structures are owned by a single class
instance and do not escape. In these cases, is it safe to do something like:
class Foo {
// Allocated on GC heap.
private HugeDataStructure hugeDataStructure;
~this() {
// hugeDataStructure _should_ be GC'd when the Foo instance
// is GC'd because hugeDataStructure is guaranteed never
// to escape. It may not be b/c
// of false pointer issues. Delete it manually when instance
// of Foo that owns it is GC'd.
delete hugeDataStructure;
}
}
The point is that the destructor for instances of Foo is called by the GC, not
manually. The GC may have already realized that hugeDataStructure is not
reachable. Does this make the delete statement in the d'tor unsafe, or is it
still ok?
Note: You can assume that hugeDataStructure doesn't have its own destructor,
so delete just frees memory.
Mar 14 2009
dsimcha wrote:I sometimes run into false pointer issues when using very large data structures in D. Often, these data structures are owned by a single class instance and do not escape. In these cases, is it safe to do something like: class Foo { // Allocated on GC heap. private HugeDataStructure hugeDataStructure; ~this() { // hugeDataStructure _should_ be GC'd when the Foo instance // is GC'd because hugeDataStructure is guaranteed never // to escape. It may not be b/c // of false pointer issues. Delete it manually when instance // of Foo that owns it is GC'd. delete hugeDataStructure; } } The point is that the destructor for instances of Foo is called by the GC, not manually. The GC may have already realized that hugeDataStructure is not reachable. Does this make the delete statement in the d'tor unsafe, or is it still ok? Note: You can assume that hugeDataStructure doesn't have its own destructor, so delete just frees memory.
http://www.digitalmars.com/d/2.0/class.html#Destructor http://www.digitalmars.com/d/1.0/class.html#Destructor both say: "The garbage collector is not guaranteed to run the destructor for all unreferenced objects. Furthermore, the order in which the garbage collector calls destructors for unreference objects is not specified. This means that when the garbage collector calls a destructor for an object of a class that has members that are references to garbage collected objects, those references may no longer be valid. This means that destructors cannot reference sub objects. This rule does not apply to auto objects or objects deleted with the DeleteExpression, as the destructor is not being run by the garbage collector, meaning all references are valid." I think you are violating that rule.
Mar 14 2009
On Sat, Mar 14, 2009 at 11:53 AM, dsimcha <dsimcha yahoo.com> wrote:The point is that the destructor for instances of Foo is called by the GC=
manually. =A0 The GC may have already realized that hugeDataStructure is =
reachable. =A0Does this make the delete statement in the d'tor unsafe, or=
still ok?
Accessing or deleting any GC-managed memory in a destructor is unsafe,
Mar 14 2009
dsimcha wrote:I sometimes run into false pointer issues when using very large data structures in D. Often, these data structures are owned by a single class instance and do not escape. In these cases, is it safe to do something like: class Foo { // Allocated on GC heap. private HugeDataStructure hugeDataStructure; ~this() { // hugeDataStructure _should_ be GC'd when the Foo instance // is GC'd because hugeDataStructure is guaranteed never // to escape. It may not be b/c // of false pointer issues. Delete it manually when instance // of Foo that owns it is GC'd. delete hugeDataStructure; } } The point is that the destructor for instances of Foo is called by the GC, not manually. The GC may have already realized that hugeDataStructure is not reachable. Does this make the delete statement in the d'tor unsafe, or is it still ok? Note: You can assume that hugeDataStructure doesn't have its own destructor, so delete just frees memory.
You can't call delete against a struct object, so the above wouldn't compile. What may solve your problem is calling GC.hasNoPointers against the block of memory in which hugeDataStructure lives. But before that... isn't the current GC non-conservative for heap-allocated objects? I thought it's only conservative for stack objects. Andrei
Mar 14 2009
== Quote from Andrei Alexandrescu (SeeWebsiteForEmail erdani.org)'s articledsimcha wrote:I sometimes run into false pointer issues when using very large data structures in D. Often, these data structures are owned by a single class instance and do not escape. In these cases, is it safe to do something like: class Foo { // Allocated on GC heap. private HugeDataStructure hugeDataStructure; ~this() { // hugeDataStructure _should_ be GC'd when the Foo instance // is GC'd because hugeDataStructure is guaranteed never // to escape. It may not be b/c // of false pointer issues. Delete it manually when instance // of Foo that owns it is GC'd. delete hugeDataStructure; } } The point is that the destructor for instances of Foo is called by the GC, not manually. The GC may have already realized that hugeDataStructure is not reachable. Does this make the delete statement in the d'tor unsafe, or is it still ok? Note: You can assume that hugeDataStructure doesn't have its own destructor, so delete just frees memory.
compile. What may solve your problem is calling GC.hasNoPointers against the block of memory in which hugeDataStructure lives. But before that... isn't the current GC non-conservative for heap-allocated objects? I thought it's only conservative for stack objects. Andrei
No, the problem is that there may be things that look like pointers pointing to internal regions of hugeDataStructure. For example, let's say hugeDataStructure is a 100-megabyte array. It's a pretty big target for false pointers, so even though the only legitimate reference is from the instance of Foo that owns it, hugeDataStructure might never get GC'd. In my specific case, hugeDataStructure is a large associative array. A few nodes don't get freed properly, leading to heap fragmentation and massive memory usage. (I've created my own AA delete function, which I know works when not used from a destructor like the above example, see Bugzilla 2105).
Mar 14 2009
dsimcha wrote:== Quote from Andrei Alexandrescu (SeeWebsiteForEmail erdani.org)'s articleYou can't call delete against a struct object, so the above wouldn't compile. What may solve your problem is calling GC.hasNoPointers against the block of memory in which hugeDataStructure lives. But before that... isn't the current GC non-conservative for heap-allocated objects? I thought it's only conservative for stack objects. Andrei
No, the problem is that there may be things that look like pointers pointing to internal regions of hugeDataStructure. For example, let's say hugeDataStructure is a 100-megabyte array. It's a pretty big target for false pointers, so even though the only legitimate reference is from the instance of Foo that owns it, hugeDataStructure might never get GC'd. In my specific case, hugeDataStructure is a large associative array. A few nodes don't get freed properly, leading to heap fragmentation and massive memory usage. (I've created my own AA delete function, which I know works when not used from a destructor like the above example, see Bugzilla 2105).
Thanks for the info, that's very interesting. Andrei
Mar 14 2009
Andrei Alexandrescu wrote:You can't call delete against a struct object, so the above wouldn't compile. What may solve your problem is calling GC.hasNoPointers against the block of memory in which hugeDataStructure lives. But before that... isn't the current GC non-conservative for heap-allocated objects? I thought it's only conservative for stack objects.
It's conservative for everything, unless you count marking an entire block as "has pointers" non-conservative. The GC currently doesn't know where in a block the pointers are, so it treats all such labeled blocks conservatively.
Mar 14 2009
Sean Kelly wrote:Andrei Alexandrescu wrote:You can't call delete against a struct object, so the above wouldn't compile. What may solve your problem is calling GC.hasNoPointers against the block of memory in which hugeDataStructure lives. But before that... isn't the current GC non-conservative for heap-allocated objects? I thought it's only conservative for stack objects.
It's conservative for everything, unless you count marking an entire block as "has pointers" non-conservative. The GC currently doesn't know where in a block the pointers are, so it treats all such labeled blocks conservatively.
Ouch. Yet another place where introspection could help lots. Any statistics about the amount of slack memory due to false pointers would be appreciated. Andrei
Mar 14 2009
== Quote from Andrei Alexandrescu (SeeWebsiteForEmail erdani.org)'sAny statistics about the amount of slack memory due to false pointers would be appreciated. Andrei
This is a corner case, but run the following code: import std.stdio, core.memory; void main() { foreach(count; 1..uint.max) { test; GC.collect; // Make it explicit to make absolutely sure GC is running. writeln(count); } } // Marked as pure to emphasize that it's not escaping someArray to anywhere, // so when test() goes out of scope, someArray is safe to GC. void test() pure { auto someArray = new uint[15_000_000]; } Disclaimer: My understanding is that this may work differently on Linux, but if you run it on Win32, it runs out of memory after about 20 iterations. I don't know about the average, but the worst cases are occasionally bad enough to make me eventually run out of 32-bit address space on stuff that should only be using ~150 megs of RAM, unless I manually delete a few large data structures. Associative arrays seem to be the worst problem because: 1. They generate a lot of false pointers. 2. They can often be large. 3. If even a few nodes don't get freed properly, they can fragment the heap severely.
Mar 14 2009
Hello dsimcha,I sometimes run into false pointer issues when using very large data structures in D. Often, these data structures are owned by a single class instance and do not escape. In these cases, is it safe to do something like:
If you can be shure ~this will get called, you might switch to malloc.
Mar 14 2009
BCS wrote:Hello dsimcha,I sometimes run into false pointer issues when using very large data structures in D. Often, these data structures are owned by a single class instance and do not escape. In these cases, is it safe to do something like:
If you can be shure ~this will get called, you might switch to malloc.
If you do this, don't forget to register and unregister the memory with the GC if it contains any pointers into GC-allocated data. Otherwise, the GC won't see those pointers and may delete that data if there aren't any other references to it.
Mar 14 2009









Chad J <gamerchad __spam.is.bad__gmail.com> 