1. Preface
In C#, with the support of the garbage collection mechanism, the destruction of objects is very different from the previous C++. This requires programmers to fully understand the .NET mechanism when designing types and clarify how to use the Dispose method and Finalize method to ensure that an object is destructed correctly and efficiently.
2. Functions of Dispose method
When we explain the usage of using, we have already introduced the Dispose method. It is precisely because the garbage collection mechanism covers the time when the object memory is actually recycled. Considering that in many cases, programmers want to do some cleaning work when the object is no longer in use, .NET provides the IDisposable interface and defines the Dispose method in it. Usually programmers will implement the release of some managed and unmanaged objects and the end of logical business in the Dispose method. Note that the implementation of the Dispose method cannot obtain any guarantees regarding release. The call of the Dispose method depends on the user of the type. When the type is used inappropriately, the Dispose method will not be called, but the existence of using and other syntax still helps the type's Dispose method be called.
3. The mechanism of Finalize method
Since the call of the Dispose method depends on the consumer, in order to make up for this defect, .NET also provides the Finalize method. Finalize method is often called a destructor by programmers with C++ development experience, but its execution method is completely different from the destructors in traditional C++. The Finalize method is called when GC performs garbage collection. The specific mechanism is as follows:
- When each instance object of type containing Finalize method is allocated, .NET adds a reference to a specific table structure and executes the instance object. For convenience, this table is called a "destructed object table".
- When GC executes and detects an object that is not used, it is necessary to further check the "destructed object table" to see if the object type has a Finalize method. If not, the object is treated as garbage. If there is a Finalize method, the reference to the object is moved from the "destructed object table" to another table, which is temporarily called "waiting for destructuring table". And the object instance is considered to be thrown in use.
- The CLR will have a separate thread responsible for handling the "waiting for destructuring tables", which is to call the Finalize method of each object through references in sequence, and then delete the reference. At this time, the object instances in the managed heap will be in a state that is no longer in use.
- When the next GC is executed, those object instances that have been called the Finalize method will be released.
4. Use the Dispose and Finalize methods correctly
The Finalize method is indeed safer than the Dispose method because it is guaranteed to be called by the CLR, but the Finalize method is much worse in terms of performance. What we need to know is that the correct type design is to use the Finalize method as a backup of the Dispose method. Only when the user does not call the Dispose method, the Finalize method can be regarded as required to be executed. Below is a correct and efficient design template. It is recommended to keep this template in mind and apply it to every type that requires DIspose and Finalize methods.
using System; namespace usingDemo { public class FinalizeDisposeBase : IDisposable { // Mark whether the object has been released private bool _disposed = false; // Finalize method ~FinalizeDisposeBase() { Dispose(false); } /// <summary> /// Here is the Dispose method in IDisposable /// </summary> public void Dispose() { Dispose(true); // Tell GC that the Finalize method of this object no longer needs to be called (true); } /// <summary> /// Do actual destruction work here /// Declare as a virtual method for subclasses to overwrite if necessary /// </summary> /// <param name="isDisposing"></param> protected virtual void Dispose(bool isDisposing) { // When the object has been destructed, it is not executed if(_disposed) { return; } if(isDisposing) { // Release managed resources here // Execute only when the user calls the Dispose method } // Release unmanaged resources here // The tag object has been released _disposed = true; } } public sealed class FinalizeDispose:FinalizeDisposeBase { private bool _mydisposed = false; protected override void Dispose(bool isDisposing) { // Guaranteed to be released only once if (_mydisposed) { return; } if(isDisposing) { // Release the managed resources declared in this type here } // Release unmanaged resources here and declared in this type // Call the Dispose method of the parent class to release the resources in the parent class (isDisposing); // Set the subclass tag _mydisposed = true; } static void Main() { } } }
The above code is a nearly perfect Dispose design template with Finalize, and there are several points that need special attention:
- The real release work is only the Virtual protected method Dispose method. In fact, the name of this method is not important. It is just for general purpose and better understanding, and it is called Dispose.
- The virtual method Dispose needs to accept a Boolean parameter, which is mainly used to distinguish whether the caller is a type consumer or .NET garbage collection. The former passes through the Dispose method of IDisposable, while the latter passes through the Finalize method. The difference between the two is that when the resource is released through the Finalize method, the managed resources in the object cannot be released or used. This is because the object is already in an unused state at this time, and it is very likely that the managed resources in it have been released.
- In the implementation of the Dispose method of IDisposable, the () method is used to tell .NET that this object does not need to call the Finalize method when it is recycled. This sentence is the key to improving performance. Remember that the essential purpose of implementing the Dispose method is to avoid all release work in the Finalize method.
- The subtype must define its own release tag to indicate whether the resources in the subclass have been released. At the same time, the virtual method Dispose method of the subclass only needs to release its newly defined resources.
- Make sure that everything you do in the virtual method Dispose is to release the work, and some logical ending work needs to be repeated consideration to prevent a simple assignment statement from causing the object to survive again.
5. Summary
The Dispose method is called actively by the user, and the Finalize method is called by a dedicated .NET thread after the first round of recycling of the object being garbage collected. The Dispose method cannot be guaranteed to be executed, while the garbage collection mechanism of .NET ensures that the Finalize method owns the Finalize method and needs to be called the type object to be executed. Calling the Finalize method involves a series of complex operations, and the performance is very costly. Programmers can notify .NET through methods that the Finalize method of the object does not need to be called. The type design of Dispose and Finalize should refer to the code template above to ensure that the object can be released efficiently and safely.
This is all about this article about the Dispose and Finalize methods in C#. I hope it will be helpful to everyone's learning and I hope everyone will support me more.