What happens if the program exits unexpectedly (either by exception or the process is terminated)? Are there any situations like this (or otherwise) where the program will terminate, but IDisposable
objects won't be properly disposed of?
The reason I'm asking is because I'm writing code that will communicate with a peripheral, and I want to make sure there's no chance it will be left in a bad state.
A very simple test using a console application illustrate that Dispose is not called on process kill:
Killing the process with Task Manager would not trigger
Disposable.Dispose()
method. Waiting for 20 seconds will.So, as already mentioned, do not rely on objects disposable when application crashes or gets killed. However, exceptions should trigger it. I am just wondering if exception such as
StackOverflowException
orOutOfMemoryException
will always trigger Dispose().[edit]
Just tested my curiosities:
StackOverflowException
gets the process terminated, so no Dispose() is calledOutOfMemoryException
allows normal call of Dispose()If the cause is an exception and thrown from within a
using
block or atry catch finally
block, it will be disposed as it should. If it is not catched by ausing
block it is not disposed automatically (like it doesn't do when the application closes properly).A sample:
d1
is not disposed,d2
usually is. Some types of exceptions may prevent handling ofusing
blocks and also some program crashes. If the cause is a power failure or system crash, there is nothing you can do either of course.Yes, there are such situations. For example, calling
TerminateProcess
, callingEnvironment.FailFast
, or encountering an internal CLR error will all cause the process to exit without running any additional code. In such situations, the best thing you can do is say "oh well".Even if the process doesn't exit unexpectedly, calling
Dispose
is a manual action. It's not something done through the runtime, except when an object implementing a finalizer that callsDispose
is garbage collected. Therefore, forgetting to wrap a disposable in ausing
or causing a memory leak that keeps the object alive is another wayDispose
may never be called.The only reliable cleanup is performed by the operating system when a process exits -- all open handles to system objects are closed. When the last handle is closed, whatever cleanup implemented in the OS or a driver happens. If this cleanup code is not part of a driver but is supposed to be called by a user process, all you can do is make your code as robust as possible, or implement a watchdog process that handles cleanup for you.
In addition to Patrick Hofman's and Alexei's answer cleanup may be not performed even if the application terminates correctly.
As you probably know the
Dispose
method is not called when the garbage collector collects the object which implementsIDisposable
interface. But the GC will call theFinalize
method also known as finalizer. In it you should write your cleanup logic using Dispose Pattern. And yes, the .Net Framework will try to run all finalizers, but there is no guaranty that they ever be executed.As an example, the program bellow has the very long running finalizer. Therefore, the .Net will terminate the process and you will never see the message.
This can be caused by any long running operation like releasing a network handle or something else which will require much time.
Therefore, you should never rely on finalizers and disposable objects. But all opened handles to kernel objects will be closed automatically, so you should not worry about them.
I will recommend you to read few interesting articles about finalizers and the GC in addition to the answers:
If the program quits unexpectedly (for example you kill the process) there are absolutely no guarantees that the
IDisposable.Dispose
method will be called. You'd better not rely on it for such events. The Dispose method must be called manually by your code, it's not something that the CLR will call automatically for you.IDisposable is just an interface. There is absolutely nothing special about the way they are handled. When you call Dispose on an IDisposable (explicitly or via a using block), it calls the contents of your Dispose method. It gets garbage collected like any other object.
The purpose of the interface is to allow an implementer to define the cleanup of a type that may have managed or unmanaged resources that need to be explicitly cleaned up.
If these resources are all managed, garbage collection may be enough and implementations may just be for optimization.
If they are unmanaged or have some connection to unmanaged resources, garbage collection is probably not enough. This is why the full recommended implementation of IDisposable involves handling both explicit disposal and disposal by the runtime (via a finalizer).
Process shutdowns will not call Dispose and finalizers are not guaranteed to run...so you have to hope that destroying the process is sufficient by itself.