Garbage collection GC:. Net Automatic Memory Management (3) Terminator

Source: Internet
Author: User

Garbage collection GC:. Net Automatic Memory Management (3) Terminator
Garbage collection GC:. Net Automatic Memory Management (3) Terminator

Preface

GC in. Net completely solves the embarrassment of developers tracking memory usage and controlling memory release. However, you may want to understand how GC works. This series of articles will explain how memory resources are reasonably allocated and managed, and contain a very detailed description of internal algorithms. At the same time, we will also discuss the GC memory cleaning process and when to clean up, and how to force clean up.


Terminator

GC provides another feature that benefits you: Terminator. After a resource is recycled, The Terminator allows an elegant cleanup operation. With the Terminator, GC can perform proper self-cleaning when releasing the memory occupied by resources.
In short: When GC detects that an object is spam, GC calls its Finalize () method (if any) and recycles the memory occupied by the object. See the following code:
Public class BaseObj {public BaseObj () {} protected override void Finalize () {// code for clearing resources // For example, close a file or connect to the Console over the network. writeLine ("In Finalize. ");}}

Now you can create an instance of this object:
BaseObj bo = new BaseObj();
In the future, if GC detects that this object has become garbage, GC will check whether it has implemented the Finalize method. If yes, it will call this method and recycle the memory occupied by this object. 

Many C ++ developers will quickly associate the destructor with the Finalize method. However, it is worth noting that they are not the same thing at all. When you try to understand the Terminator, you 'd better forget all the operations related to destructor. The managed object never has a destructor cycle. When designing a type, it is best to avoid using the Finalize method. The following are several reasons:
  • Objects that can be terminated (Finalize) will be promoted to the older generation of GC, which will increase the memory pressure and prevent object memory from being recycled even if GC considers this object as a spam object. In addition, all objects that have direct or indirect relationships with this object will also be promoted. The generation and generation improvements in GC will be introduced in subsequent articles.
  • Objects that can be terminated (Finalize) need to be allocated for a longer time.
  • The Finalize method of force GC execution will significantly reduce the performance. Therefore, if there are 10000 objects that implement the Finalize method, GC must execute its 10000 termination method, compromising performance.
  • The object that implements the terminator may reference objects without the Terminator, resulting in the extension of the lifecycle of objects without the Terminator. In fact, you may want to divide a type into two different types: the lightweight type with a Terminator that does not reference any other object and the type that references other objects without a Terminator.
  • You cannot control the execution time of the terminator method. Therefore, it may occupy a certain amount of resources and will not be released until the next GC recycle.
  • When a program is terminated, some objects can always be accessed and the Terminator is not executed. For example, the object used by the background thread or the object created during program termination (or program domain uninstallation. In addition, by default, in order for the program to terminate quickly, when a program ends, it will not call the terminator of an object that cannot be accessed. Of course, all operating system resources will be recycled, but objects in the managed heap cannot be properly cleared. If you want to change the default behavior, you can call the RequestFinalizeOnShutdown method of System. GC. However, you must be careful when using this method, because calling this method means that your type is controlling the policies of the entire application.
  • The execution sequence of the final node cannot be guaranteed when the program is running. For example, if an object contains a pointer pointing to an internal object, GC checks that both objects are junk. Furthermore, the terminator of the internal object is called first. Now, the terminator of the external object can access the internal object and call its method, but the internal object has been terminated. The result is unpredictable. For this reason, we strongly recommendThe Terminator does not access any internal Member objects..
If you decide to implement the terminator for your type, make sure that the code can be executed as quickly as possible. This avoids the actions that may prevent the terminator from executing, including thread synchronization operations. On the other hand, if you throw an exception in the Terminator, the system will think that the terminator has returned (executed), and then continue to execute the final server of other objects. Of course, when the compiler generates code for the constructor, the compiler will automatically insert a call to the base class constructor. Similarly, when a C ++ compiler generates code for the destructor, the compiler automatically inserts a call to the base class destructor. However, as mentioned earlier, the Terminator and the Destructor are different. The compiler does not have special processing for the Terminator. Therefore, the compiler does not automatically generate code to call the terminator of the base class. To implement this process, you must explicitly call the base class terminator in your Terminator:
Public class BaseObj {public BaseObj () {} protected override void Finalize () {Console. writeLine ("In Finalize. "); base. finalize (); // call the base class Terminator }}
In the derivative type Terminator, you will often call the terminator of the base class in the last code sentence. In this way, the base class object can be kept as long as possible. This is because it is common to call the base class Terminator. C # has a simple Syntax:
Class MyObject {~ MyObject () {// other code }}
Causes the compiler to generate this code:
Class MyObject {protected override void Finalize () {// other code base. Finalize ();}}


This is similar to the C ++ parser, but remember that C # does not support the parser.


Inside the Terminator

On the surface, the Terminator looks straightforward: you create an object with a Terminator. When it is recycled, The Terminator is called. In fact, you cannot see more operations.

When an application creates a new object, the new operator allocates memory to it in the heap. If there is a Terminator, a pointer to this object will be put into the terminator queue. The Terminator queue is an internal data structure controlled by GC. Each item in the queue points to an object, which calls the terminator before the memory is recycled.
Several objects are stored in the heap. Some objects can be accessed by the program root, and some cannot. When objects C, E, F, I, and J are created, the system detects that these objects implement the Terminator and adds pointers to these objects in the terminator queue.

Hosting heap with many objects (for questions about heap and stack, refer to: simple illustration C # heap and stack ):
Finalization Queue: Terminator Queue;

When GC recycles memory, objects B, E, G, H, I, and J are considered as garbage. GC scans the terminator queue to check whether there are pointers pointing to these objects. If so, move the pointer to the terminator queue and move it to the terminator reachable queue. The Terminator reachable queue is another internal data structure controlled by GC. Each pointer in the Terminator's reachable queue represents an object that has already executed the Terminator.

After GC recycles the memory, the managed heap becomes. You can see that the memory occupied by object B, G, and H has been recycled because they do not have a Terminator. However, the memory occupied by Object E, I, and J is not recycled because their Terminator is not executed yet.
Managed stacks after GC collection:
Finalization Queue: Terminator Queue; Freachable Queue: Terminator reachable Queue

There is a special runtime thread dedicated to calling the Terminator. Normally, when the Terminator's reachable queue is an empty queue, this thread goes to sleep. However, once the terminator reaches the queue, a new item appears, this thread wakes up, removes the terminator to reach all the items in the queue and calls their Terminator. Because of this mechanism, you cannot execute any Encoding Based on this thread in the Terminator. For example, do not access the local memory of the thread in the final node.
The interaction between the terminator queue and the terminator reachable queue is very interesting. F In Freachable represents the Finalization Terminator, and reachable indicates that the object can be accessed. The Terminator reachable queue is considered to be the same root as global variables and static variables. Therefore, GC determines that the Terminator is reachable and all objects in the queue are not spam.

All in all, when an object with a terminator cannot be accessed, GC considers it as garbage. Then, GC will move the pointer of this object in the terminator queue to the terminator reachable queue. At this time, this object will no longer be junk, and the memory occupied by it will not be recycled. So far, GC has completed a spam scanning and identification process. GC compression can release the memory. The special runtime thread can clean up the terminator to reach the queue and execute the terminator of each object.

When GC is used for secondary garbage collection, the garbage object with a terminator becomes real garbage because the program root does not point to it, and the Terminator's reachable queue does not point to it. In this case, the memory occupied by this object will be recycled. The point I want to point out here is the object with a Terminator. GC needs to recycle them twice to recycle the memory they occupy. In fact, GC sometimes needs to perform garbage collection more than twice because the object may be promoted to an older generation. The managed heap after GC secondary recovery is displayed.
Managed stacks after secondary GC recovery:



To sum up the relationship between Terminator and GC, We need to master and understand. This helps you better understand the GC mechanism of garbage collection. Of course, there is more content than the Terminator. The next section will introduce "Resurrection and forced recovery".



Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.