digitalmars.D.learn - Object destruction versus finalization
- Florian (17/17) Nov 12 2013 I played around a little and figured out, that destructors in D
- Dicebot (6/12) Nov 12 2013 What made you think so? It must be other way around. Destructor
- Florian (41/53) Nov 12 2013 Let me explain this by discussing the example below. I created
- Jonathan M Davis (9/11) Nov 12 2013 Classes in D do not have destructors. Only structs to. ~this is a destru...
- Florian (3/3) Nov 12 2013 I understood very well, that the garbage collector is not
- Jonathan M Davis (13/16) Nov 12 2013 You're getting a segfault, because you're using something which is on th...
- Dicebot (7/11) Nov 12 2013 Same example prints this for me (no segfault):
- Michael (4/4) Nov 13 2013 Additional discussions
- Florian (3/3) Nov 14 2013 Michael,
I played around a little and figured out, that destructors in D work quite similarily to destructors in C++. They are invoked, after the members of the instance being destructed have been destroyed themselfes (or at least have been brought into an invalid state). Therefore, these members cannot be accessed savely from inside the destructor. the finalizer is called, all members of the instance itself are still valid and are guaranteed to be freely accessible. This behaviour can be useful for some cleanup operations. destructors, where the destructor is called ~className and the finalizer is called !className. Is their a best practice to mimikry this behaviour, i.e. to call a certain block of code when an object is being garbage collected, but before its contents render invalid? Any hints would be greatly appreciated.
Nov 12 2013
On Tuesday, 12 November 2013 at 20:15:02 UTC, Florian wrote:I played around a little and figured out, that destructors in D work quite similarily to destructors in C++. They are invoked, after the members of the instance being destructed have been destroyed themselfes (or at least have been brought into an invalid state). Therefore, these members cannot be accessed savely from inside the destructor.What made you think so? It must be other way around. Destructor is expected to release resources held by object, it is necessary that those resources are still valid at destructor call point. It would have been completely against the mode of operations of garbage collector.
Nov 12 2013
On Tuesday, 12 November 2013 at 20:29:13 UTC, Dicebot wrote:On Tuesday, 12 November 2013 at 20:15:02 UTC, Florian wrote:Let me explain this by discussing the example below. I created method stubs, which write single lines to the console, so it is quite easy to follow the control flow. The main method creates an instance of a type "Session", which itself has a member of type "Connection". Let us assume, that I want to properly shutdown the "Connection" instance by invoking its methods, when the "Session" is torn down. The example below prints the following output: ~Connection ~Session segmentation fault This means, the destructor of "Connection" is invoked *BEFORE* the destructor of "Session" is called. This yields to an invalid reference for the call of the shutdown() method, leading to the segmentation fault. Of course, in a scenario as simple as that, it would be possible to move the shutdown() sequence into the destructor of the "Connection" class. However, doing so is not desirable. Just picture, that we want to set some timeout parameters or the like for the shutdown depending on the state of the "Session". This would become really messy. And be assured, I have a more complex scenario, where I definitely want to invoke methods on memeber instances when an instance is destroyed. I am and managed C++ do. Example: import std.stdio; class Connection { this() { } void shutdown() { writeln("shutdown"); } ~this() { writeln("~Connection"); } } class Session { Connection connection; this() { connection = new Connection(); } ~this() { writeln("~Session"); connection.shutdown(); // -> segmentation fault } } void main() { auto session = new Session(); }I played around a little and figured out, that destructors in D work quite similarily to destructors in C++. They are invoked, after the members of the instance being destructed have been destroyed themselfes (or at least have been brought into an invalid state). Therefore, these members cannot be accessed savely from inside the destructor.What made you think so? It must be other way around. Destructor is expected to release resources held by object, it is necessary that those resources are still valid at destructor call point. It would have been completely against the mode of operations of garbage collector.
Nov 12 2013
On Tuesday, November 12, 2013 23:40:24 Florian wrote:it would be possible to move the shutdown() sequence into the destructor of the "Connection" class.Classes in D do not have destructors. Only structs to. ~this is a destructor in a struct, but it's a finalizer in a class. Finalizers are not guaranteed to be run, and they can't do anything with GC memory (be it allocating it, deallocating it, or using it) unless they're begging for trouble, because the GC is free to collect any GC-allocated objects before calling any finalizers (which avoids circular reference problems). As such, class finalizers are really only good for managing non-GC resources. - Jonathan M Davis
Nov 12 2013
I understood very well, that the garbage collector is not guaranteed to run. However, it does not explain the segmentation fault in my example, does it?
Nov 12 2013
On Wednesday, November 13, 2013 00:07:12 Florian wrote:I understood very well, that the garbage collector is not guaranteed to run. However, it does not explain the segmentation fault in my example, does it?You're getting a segfault, because you're using something which is on the GC heap - namely the Connection that you allocated with new. You can't do that in finalizer, because the GC can choose to free it before the finalizer even runs (this avoids issues with circular references). You can only access stuff in the finalizer which is part of the class (and not on the GC heap aside from the fact that the instance of the class is itself on the GC heap) or which is not managed by the GC at all (e.g. it was malloced, or it was some other sort of system resource that the GC doesn't manage). If you want your Connection object to shutdown when it's collected, you're going to need to do that in its own finalizer, not in the finalizer of a class that's using it. - Jonathan M Davis
Nov 12 2013
On Tuesday, 12 November 2013 at 23:18:11 UTC, Jonathan M Davis wrote:You can't do that in finalizer, because the GC can choose to free it before the finalizer even runs (this avoids issues with circular references).Ah, damn, have forgotten about it. Disregard previous post.
Nov 12 2013
Jonathan, Dicebot, thank you very much for your response. So you are confirming my conclusion, that finalizers/destructors in D work pretty much finalization. For the records, since Dicebot asked: I am using DMD32 v2.063.2 on Debian 7.2 All the best, Florian.
Nov 13 2013
On Tuesday, 12 November 2013 at 22:40:26 UTC, Florian wrote:The example below prints the following output: ~Connection ~Session segmentation faultSame example prints this for me (no segfault): ~Session shutdown ~Connection 2.064.2 linux-64 What is your system / compiler? Output does not seem right at all.
Nov 12 2013
Additional discussions http://forum.dlang.org/thread/jpl93e$1mns$1 digitalmars.com and http://forum.dlang.org/thread/l3dj7b$2tvc$1 digitalmars.com
Nov 13 2013
Michael, thank you for these links. Regards, Florian.
Nov 14 2013