问题
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?
回答1:
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.
回答2:
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.
回答3:
.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.
回答4:
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.
来源:https://stackoverflow.com/questions/9941688/arent-destructors-guaranteed-to-finish-running