On Monday, 17 December 2012 at 10:39:56 UTC, Jeremy DeHaan wrote:

I tried putting destroy in the destructor like the code I wrote. Like I said, it gave me no memory errors, but I don't actually know if it even does anything. I honestly think that it is weird to not be able to rely on destructors like this. To me, it makes sense that the destructor would be a part of the garbage collection, but again I don't have any sort of experience with memory management. I find it kind of silly to have something like a destructor built into a language, but it not being reliable and more or less something to be completely ignored altogether. I do understand that in most cases this can be so, as the GC will free up the memory itself, but in some cases it could be useful to know your destructor will free up the resources the GC can't.

I've not yet used destroy and am not familiar with everything it does, but I do know that it isn't going to help you with an sfImage because the memory for that was not allocated by the GC.

What I do know about destroy is that if you do want some control over when destructors are called then destroy is what you would use to do it. destroy(myClassInstance) will call the destructor of the object. But, it will not deallocate the object's memory. Unlike the deprecated delete, object destruction and memory deallocation have been decoupled with destroy.

However, if you are going to take this route, then you have to make sure that every object which releases resources in its destructor is manually destroyed. This is no different than what I do with my term() methods. But I like my approach better. All objects have destructors, but only those that need to release resources have a term() method, so I'm less likely to make silly mistakes at 3 am after several hours of coding.

At any rate, unreliable destructors are usually what you get when you are using a GCed language (or none at all, like in Java). They're not completely useless, though.



The scope(exit) in the main method ensures that this termination chain is run every time the app exits regardless of the reason

This is good to know. I read about this, but haven't used it yet. Wouldn't the destructors get called at this point?

No. Whatever code you have in the scope block gets called.

import std.stdio;

scope(exit) writeln("Foo");

scope(success)
{
   writeln("Bar");
   writeln("Baz");
}

All that's happening here is that the code you've included in the scope blocks will be called. The first block will execute no matter how the scope exits. The second block will only execute when the scope exits normally (and not due to an exception). The output will be:

Bar
Baz
Foo

Because the scope blocks are always executed in the reverse order they are encountered. scope(failure) will run when exiting due to an error.

So with these statements, there's nothing going on under the hood to call destructors or release memory.




Also, what about something like this?

void main(string[] args)
{
   {
      init();
      gameLoop();
}<-Wouldn't the destructors get called here? Or would the GC run first? }<-And what would happen here? Would GC be run again after the scope is left?

The main method in your D module is not the program entry point. The real entry point is in DRuntime. See the dmain2.d module in the Druntime source. At line 367 (in the current master) you'll see the entry point.

https://github.com/D-Programming-Language/druntime/blob/master/src/rt/dmain2.d#L367

extern (C) int main(int argc, char **argv)
{
    return _d_run_main(argc, argv, &_Dmain);
}

Looking in _d_run_main, you'll see a bit of platform-specific set up code for each platform, then a series of inner functions. At the bottom of _d_run_main is this line:

tryExec(&runAll);

runAll is an innder function inside _d_run_main, defined just above this tryExec call. It looks like so:

    void runAll()
    {
        gc_init();
        initStaticDataGC();
        rt_moduleCtor();
        rt_moduleTlsCtor();
        if (runModuleUnitTests())
            tryExec(&runMain);
        else
            result = EXIT_FAILURE;
        rt_moduleTlsDtor();
        thread_joinAll();
        rt_moduleDtor();
        gc_term();
    }

It initializes the GC, then calls the module constructors, then runs unit tests, then calls your main function. When your main function returns, module destructors are run, then gc_term is called to finalize the GC. It is here that any objects still hanging around in the GC that have not be destructed will have their destructors called and then all the memory is released.





I am just trying to understand the language better. So far it seems that the only way to prevent memory leaks when memory is being allocated outside of D is to manually delete those objects yourself and to not rely on the destructors. I will make sure this happens in the future!

It's not the only way, but it's the easiest way.


Sorry if I say silly things. I am but a young programmer trying to gain knowledge!

Reply via email to