Aren't destructors guaranteed to finish running?
Asked Answered
O

4

8

Destructors are weird. I was attempting to eliminate the need of using the disposable pattern by using 'smart' reference management, ensuring that the garbage collector could collect objects at the correct time. In one of my destructors I had to wait for an event from another object, which I noticed it didn't. The application simply shut down and the destructor was terminated in middle of execution. I'd expect a destructor is always allowed to finish running, but as the following test indicates that is not true.

using System;
using System.Diagnostics;
using System.Threading;


namespace DestructorTest
{
    class Program
    {
        static void Main( string[] args )
        {
            new DestructorTest();
            new LoopDestructorTest();
            using ( new DisposableTest() ) { }
        }
    }

    class DestructorTest
    {
        ~DestructorTest()
        {
            // This isn't allowed to finish.
            Thread.Sleep( 10000 );
        }       
    }

    class LoopDestructorTest
    {
        ~LoopDestructorTest()
        {           
            int cur = 0;
            for ( int i = 0; i < int.MaxValue; ++i )
            {
                cur = i;
            }
            // This isn't allowed to finish.
            Debug.WriteLine( cur );
        }
    }

    class DisposableTest : IDisposable
    {
        public void Dispose()
        {
            // This of course, is allowed to finish.
            Thread.Sleep( 10000 );
        }
    }
}

So, aren't destructors guaranteed to finish running?

Oid answered 30/3, 2012 at 11:1 Comment(6)
Waiting for an event from another object within a finalizer? This is madness!Cilicia
You really should stick with the IDisposable pattern to have reliable destruction and resource release.Spinous
@Cilicia ... and waiting for an event from another object to continue triggering Dispose() isn't madness? What's the difference?Oid
msdn.microsoft.com/en-us/library/…Gear
@StevenJeuris I did not imply that at all.Cilicia
link Read the second bullet under The Finalize method might not run to completion or might not run at all in the following exceptional circumstances:Kolivas
A
16

So, aren't destructors guaranteed to finish running?

No. From what I remember, when the process terminates it gives finalizers a couple of seconds to execute, but then terminates the process abruptly. You wouldn't want a bad finalizer to prevent a process from ever finishing, would you?

You should regard finalization as a "best effort" clean-up - in particular, it's not going to happen in situations where the whole system is abruptly shut down, such as BSOD or power outage.

EDIT: I've found some pseudo-documentation in the form of a blog post from Joe Duffy:

If a lock was orphaned in the process of stopping all running threads, then, the shutdown code path will fail to acquire the lock. If these acquisitions are done with non-timeout (or long timeout) acquires, a hang will ensue. To cope with this (and any other sort of hang that might happen), the CLR annoints a watchdog thread to keep an eye on the finalizer thread. Although configurable, by default the CLR will let finalizers run for 2 seconds before becoming impatient; if this timeout is exceeded, the finalizer thread is stopped, and shutdown continues without draining the rest of the finalizer queue.

Accuse answered 30/3, 2012 at 11:5 Comment(6)
Actually I would, or at least I would like to get a big massive warning in Visual Studio the application didn't finalize correctly (as in not all destructors were allowed to finish running).Oid
@StevenJeuris: It sounds like you're relying on finalizers in a situation where you really shouldn't. Finalizers should help to avoid resource leaks - if your system will end up in a bad state if they don't run to completion, you should redesign. After all, there's always the possibility that they won't run due to a BSOD, power outage etc.Accuse
Well the redesign of course involves using IDisposable, which isn't terminated abruptly because it has to wait x ms for an event which indicates a disposal step is finished and the next step can be executed. I just don't see the point of destructors then. In what situation should you rely on them?Oid
@StevenJeuris: Rely on them to always execute, for correctness in persistent state outside your process? Never, as far as I'm concerned. You should always consider that it's possible for your system to simply stop executing. You can rely on them to clean-up resources which don't need to be eagerly released, within a process. Personally I can't remember the last time I wrote a finalizer...Accuse
A system failure isn't of any concern to me, neither is persistency. I was just attempting to clean up unmanaged memory without having to worry when (e.g. using or calling Dispose()). Your answer makes clear why that always has to happen through IDisposable. It also makes clear that even though calling Dispose() in the destructor is what the pattern advices, it's only a meager last resort. I didn't want to 'eagerly' release those unmanaged resources, I just wanted to release them when the GC wanted to. Thank you for the explanation! I'll no longer use them either.Oid
@StevenJeuris: If it's resources within the process, it doesn't matter anyway, does it?Accuse
B
1

So, aren't destructors guaranteed to finish running?

Although its not in your code there can be instances where a GC.SuppressFinalize is explicitly called from Dispose() method. This supresses the finalization, and is for objects that does not require it.

This can improve performance significantly, as finalizable objects will always survive one garbage collection, i.e. it will be promoted to a gen1 or even a gen2 GC, which has a greater cost associated with it.

Bitumen answered 30/3, 2012 at 11:13 Comment(0)
B
1

.NET doesn't come with destructors. Your code contains finalizers instead.

Finalizers are called when the object is garbage collected, not when it is nullified. They also get limited execution time to prevent hanging objects.

See also https://en.wikipedia.org/wiki/Finalizer.

Butts answered 30/3, 2012 at 11:16 Comment(3)
But C# definitely does have destructorsFootpoundsecond
The people who wrote the C# spec have decreed that the syntactical element indicated with a tilde followed by a class name is officially called a destructor. I happen to think the destructor syntax and terminology are both silly, since (1) the term "destructor" was already used by other languages, to mean something different and unrelated; (2) the term "finalizer" exists and describes what the C# destructor is generally trying to do, though a destructor wraps a finalizer in some other code; (3) proper use of a destructor generally requires GC.SuppressFinalize() and GC.KeepAlive()....Zebe
...so unless there exist frameworks which support the latter two functions but perform finalization via some method other than overriding Object.Finalize, use of a destructor is no more portable than would be overriding Object.Finalize, if C# permitted the latter course of action.Zebe
H
0

Another way to look at is is that the finalizers are called when the garbage collector frees up memory.

However if you had a program that at most required 1Mb of memory but was running on a machine with 10Mb of memory then a valid implementation of a grabage collector would be to do nothing (as there would be enough memory for the program to execute). In that case no finalizers would ever be called.

Handcraft answered 20/8, 2015 at 12:55 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.