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!