. NET garbage collection-unmanaged Resources
In C # development, most resources can be passed. the. NET garbage collection mechanism is used only when we use unmanaged resources (original operating system file handle, original unmanaged database connection, and unmanaged memory, we only need to implement our own resource cleaning code.. NET provides two methods to release unmanaged resources, including the Finalize method and the Dispose method of the IDisposable interface. Next let's take a look at the two methods related to garbage collection. The Finalize method defines a virtual method named Finalize () in the base class System. Object of. NET. This method does nothing by default. We can rewrite the Finalize method for the custom type and add the necessary unmanaged resource clearing logic to the method. When you delete an object of this type from the memory, the garbage collector calls the Finalize method of the object. Therefore, the Finalize method is always called no matter whether. NET carries out a spontaneous garbage collection or we use GC. Collect () for forced garbage collection. In addition, when the AppDomain that carries the application is removed from the memory, the Finalize method is also called. Rewrite the Finalize method. If we have a type that uses unmanaged resources, we need to rewrite the Finalize method to clean up unmanaged resources, however, when we rewrite the Finalize method in the following way, we will get a compilation error. Class MyResourceWrapper {protected override void Finalize () {}} actually, when we want to rewrite the Finalize method, C # provides us with (similar to C ++) the Destructor syntax (C # Terminator) to override this method. C # similar to the constructor syntax, the method name is the same as the type name. The difference is that the terminator has ~ Prefix, and cannot use access modifiers. parameters are not accepted or overloaded. Therefore, a class can only have one Terminator. Class MyResourceWrapper {~ MyResourceWrapper () {Console. writeLine ("release unmanaged resources"); Console. beep () ;}} C # only supports Finalize method rewriting in this way because the C # compiler implicitly adds necessary basic code to the Finalize method. The following shows the IL code through ILSpy. The code in the scope of the Finalize method is placed in a try block, and no matter whether an exception occurs in the try block, the finally block ensures that the Finalize method can always be executed.. Method family hidebysig virtual instance void Finalize () cel managed {// Method begins at RVA 0x2050 // Code size 31 (0x1f ). maxstack 1. try {IL_0000: nop IL_0001: ldstr "release unmanaged resources" IL_0006: call void [mscorlib] System. console: WriteLine (string) IL_000b: nop IL_000c: call void [mscorlib] System. console: Beep () IL_0011: nop IL_0012: nop IL_0013: leave. s IL_001d} // end. try fin Ally {IL_0015: ldarg.0 IL_0016: call instance void [mscorlib] System. object: Finalize () IL_001b: nop IL_001c: endfinally} // end handler IL_001d: nop IL_001e: ret} // end of method MyResourceWrapper: Finalize when we execute the following code, we can hear the system beep. As we described earlier, the AppDomain is removed from the memory and the type Terminator will be called. Static void Main (string [] args) {MyResourceWrapper mr = new MyResourceWrapper ();} The Finalize Working mechanism is still complicated. Here is a brief introduction, for more principles, you can check them online. When an object space is allocated on the managed stack, the runtime automatically determines whether the object provides a custom Finalize method. In this case, the object is marked as final, and a pointer to this object is saved in the internal queue named terminate queue. An ending queue is a table maintained by the garbage collector. It points to each object that must be terminated before it is deleted from the stack. When the Garbage Collector determines the time when an object is released from the memory, it checks every item in the end queue, and copy the object from the stack to another managed structure called finalization reachable table. In this case, another thread is generated during the next garbage collection, and the Finalize method is called for each object in the reachable table. Therefore, to truly end an object, at least two garbage collections are required. As you can see above, the call of the Finalize method is quite resource-consuming. The Finalize method ensures that the. NET object can clear unmanaged resources during garbage collection. If a type that does not use unmanaged resources is created, the terminator can be implemented without any effect. Therefore, if there are no special requirements, we should avoid rewriting the Finalize method. The IDisposable interface can be used to release unmanaged resources when garbage collection takes effect. However, many unmanaged resources are very valuable (such as databases and file handles), so they should be cleared as quickly as possible, rather than relying on garbage collection. In addition to rewriting Finalize, the class can also implement the IDisposable interface, and then actively call the Dispose method in the code to release resources. Let's take an example: class MyResourceWrapper: IDisposable {public void Dispose () {Console. writeLine ("release resources with Dispose"); Console. beep () ;}} class Program {static void Main (string [] args) {MyResourceWrapper mr = new MyResourceWrapper (); mr. dispose () ;}} Similarly, when we call the Dispose method, we can hear the system beep. Note: releasing resources through Dispose also has potential risks, because the Dispose method needs to be called by the programmer, if the call to Dispose is missing in the Code or an exception occurs before the call to Dispose and no Dispose is specified, some resources may remain in the memory. Therefore, we should use the following method to ensure that the Dispose method can be called: static void Main (string [] args) {MyResourceWrapper mr = new MyResourceWrapper (); try {// do something wiht mr object} finally {mr. dispose () ;}} however, every time you write the Dispose code using try block, it will be very troublesome. Fortunately, in C #, We can reuse the using keyword to simplify the call of Dispose. Reuse the using keyword in C #. The using statement provides an efficient way to call the Dispose method of the object. The using statement can be used for all types of IDispose interfaces. However, for those types that do not implement the IDisposable interface, using the using statement may cause a compilation error. Static void Main (string [] args) {using (MyResourceWrapper mr = new MyResourceWrapper () {// do something with mr object} at the end of the using statement block, the mr instance's Dispose method will be automatically called. The using statement not only saves the programmer from entering the code called by Dispose, but also ensures that the Dispose method is called, whether the execution of the using statement block ends smoothly or throws an exception. In fact, the C # compiler automatically adds a try/finally block to the using statement. Let's take a look at the using IL code :. try {IL_0007: nop IL_0008: nop IL_0009: leave. s IL_001b} // end. tryfinally {IL_000b: ldloc.0 IL_000c: ldnull IL_000d: ceq IL_000f: stloc.1 IL_0010: ldloc.1 IL_0011: brtrue. s IL_001a IL_0013: ldloc.0 IL_0014: callvirt instance void [mscorlib] System. IDisposable: Dispose () IL_0019: nop IL_001a: endfinally} // The combination of end handler Dispose and Finalize can be automatically called through garbage collection, dispose Calls that need to be displayed by code. Therefore, for the sake of security, it is necessary to implement the terminator for some unmanaged resources. That is to say, if we forget to display the call to Dispose, garbage collection will also call Finalize to ensure the collection of unmanaged resources. In fact, MSDN provides us with a good mode to implement the IDisposable interface to combine Dispose and Finalize. For example, the following code: class MyResourceWrapper: IDisposable {private bool IsDisposed = false; public void Dispose () {Dispose (true); // tell GC not invoke Finalize method GC. suppressFinalize (this);} protected void Dispose (bool Disposing) {if (! IsDisposed) {if (Disposing) {// clear managed resources} // clear unmanaged resources} IsDisposed = true ;}~ MyResourceWrapper () {Dispose (false) ;}} In this mode, the void Dispose (bool Disposing) function uses a Disposing parameter to determine whether it is currently called by Dispose. If it is called by Dispose (), you need to release both hosted and unmanaged resources. If it is called by the Terminator, you only need to release the unmanaged resources. The Dispose () function is explicitly called by other code and requires resource release, while Finalize is called by GC. In addition, because managed and unmanaged resources have been released in Dispose (), it is unnecessary to call Finalize again when the object is recycled by GC. Therefore, in Dispose (). suppressFinalize (this) prevents repeated calls to Finalize. Similarly, because the IsDisposed variable exists, the resource is released only once, and redundant calls are ignored. Therefore, the advantages of this mode can be summarized as follows: if the call Dispose () is not displayed and the managed and unmanaged resources are not released, Finalize () will be executed during garbage collection (), releases unmanaged resources. At the same time, GC releases managed resources. If Dispose () is called, managed and unmanaged resources can be released in a timely manner. When this object is reclaimed, finalize () is not executed, which improves the efficiency of using unmanaged resources and improves the system performance. NET: Dispose and Finalize. Finalize is used to release unmanaged resources, while Dispose is used to release all resources, including hosted and unmanaged resources. Dispose must be called in the Code, while Finalize is automatically called by garbage collection. In order to effectively combine Dispose and Finalize, this article also introduces a pattern for implementing the IDisposable interface in MSDN.