Chapter 8 object Lifecycle
This chapter describes how CLR manages allocated objects through garbage collection .. NET objects are allocated to a memory area called the managed heap, where they will be automatically destroyed by the garbage collector (GC) at a certain time in the future. Later I learned about the System. GC type by programming the garbage collector. Then, the types of the System. Object. Finalize () virtual method and the IDisposable interface are analyzed to release internal unmanaged resources in a timely manner.
8.1Class, object, and reference
A class is a blueprint that describes what the type of instance (object) looks like in the memory. A class is defined in a code file.
After defining a class, you can use the new Keyword of C # to allocate any number of objects and allocate memory for them in the managed heap. However, the new keyword returns a reference to the object on the stack, rather than the real object itself. This reference variable is stored in the stack for use by the application. Therefore, an object consists of a reference in the stack and an object entity in the managed stack.
8.2Memory Management Rules
When creating a c # application, you do not need to directly operate on the managed heap. It will manage it on its own .. . NET memory management rules include four.
(1) Rule 1: use the new keyword to allocate an object to the managed heap.
(2) If the managed heap does not have enough memory to allocate the requested object, garbage collection will be performed.
(3) The only reason for rewriting Finalize is that the c # class uses unmanaged resources through pinvoke or complex COM interoperability tasks.
(4) If an object supports IDisposable, always call Dispose () for any directly created object, you should consider that if the class designer chooses to support the Dispose method, this type requires cleaning.
8.3Object declaration cycle BASICS (Rule 1)
According to rule 1, after the instantiation, GC will destroy the object when it is no longer needed. How can we determine that an object in the managed heap is no longer needed? Simply put, an object is deleted from the heap when it is removed from any part of the code base. For example, for an object defined in a method, and this object is not passed outside the scope of the method definition, the reference of this object will not be reachable after the method call is completed, this object is a candidate target for garbage collection. However, it cannot be guaranteed that the target will be recycled immediately after it becomes a candidate recycling target. It is certain that the CLR will be completely destroyed during the next garbage collection.
Question: "It is certain that the CLR will be completely destroyed during the next garbage collection .", Who knows whether it is simply marked as the next generation or actually recycled ??
Automatic garbage collection greatly simplifies application development and avoids Memory leakage (memory is not released in time, leading to the failure to try again and waste.
The New Keyword corresponds to a newobj command in CLR. To assist in hosting heap garbage collection, the managed heap saves a pointer (called the next object pointer or New object pointer ), it accurately indicates the location where the next object will be allocated.
After newobj sends a command, CLR executes the following core tasks:
1) calculate the total memory required for the allocated object (including the memory required for member variables and types of base classes ).
2) Check the managed heap to ensure that there is sufficient space to access the allocated objects. If so, call the Type constructor and allocate space for it in the managed heap, return the current next object pointer as a reference to this object to the caller, and move the pointer of the next object to the next available location of the managed heap. If there is not enough space, follow law 2, it will implement a garbage collection to try to release the memory. After it is released, it will have a pointer to the next object, and then allocate it according to the logic with enough space on it.
8.4Garbage collection process (rule 2)
When a collection occurs, GC suspends all active threads in the current process to ensure that the application will not access the heap during the collection process. Once the collection cycle is completed, the suspended threads are allowed to continue their work. Due to the careful Optimization of GC, users are rarely aware of program interruptions.
As we have already said, garbage collection will happen at a certain time in the future, and this process is often uncertain (unless forced garbage collection). Therefore, some programmers use the previous c ++ method to set an object that is no longer used to be null and think that this can be immediately recycled and the memory is released. This idea is wrong, in c #, assigning the reference value to null does not mean that the GC is started immediately and the object is removed from the heap, it only shows that the connection between the reference and the object to which the previous reference is directed is canceled (I think you can no longer access this object, because the reference is empty, however, this object still exists. In addition, because the reference in the stack is set to null, the stack is immediately popped up to become a zone !). Therefore, setting null does not make much sense, but it does not do anything.
When garbage collection is started, it depends on two important concepts: application root and object generation.
Application root: As mentioned earlier, when an object is inaccessible, it can be used as an alternative for garbage collection. But how can we determine that the object is not reachable? This requires the application and the root is a storage location, saves the reference to the object on the heap.
During garbage collection, the first run library will check the objects in the managed heap (and any internal object references they may contain) to determine if the application can still reach them, that is, whether there is a root or whether there is an active root. The root is generated based on the object graph. An object graph contains every object on the heap, and does not make the same object appear more than once in the object graph. Once an object graph is generated, inaccessible objects are marked as garbage. After being marked as an end, they will be cleared from the memory (not all are cleared. If the space is released enough, it may not be cleared temporarily. This can be explained in the object generation below ), the remaining space is compressed (called a relocation), causing CLR to modify the set of active application root, pointing to the correct memory location, and finally, the pointer to the next object is adjusted to point to the next available location.
In fact, GC uses two different stacks, one dedicated for storing very large objects, which requires less garbage collection because of the high overhead of repositioning.
Object generation: As mentioned above, during garbage collection, CLR checks whether the object is reachable, but does not check every object in the managed heap. Obviously, all checks consume a lot of time. To optimize this process, each object on the stack is specified as a "Generation". The design idea is that the longer the object exists in the stack, the more likely it should be retained. Each heap object belongs to one of the following generations:
Generation 1: never marked as a new allocated object for recycling;
Generation 1: objects not recycled in the previous garbage collection (that is, they are marked as recycled, but are not deleted because they have obtained sufficient space );
2nd generation: the objects that have not been recycled after the previous garbage collection.
When checking objects, first check the 0th generation. If you mark and clear these objects and get the required amount of idle memory, any objects not recycled will be upgraded to the 1st generation. If you still need more memory after calculating all objects in the 0th generation, the "accessibility" of the 1st generation object will be checked and recycled accordingly. The 1st generation objects that were not recycled were subsequently upgraded to the 2nd generation. If more memory is needed, the 2nd-generation object accessibility will be checked. At this time, if a 2nd-generation object still exists after garbage collection, it will still be a 2nd-generation object, because this is the upper limit of the object generation.
Q: "If we still need more memory after counting all the objects on the 0th generation," Why should we count all the object memory instead of the remaining 0th generations of free space?
Q: Is it still not enough after 2nd-generation recovery?
The main point here is to assign a value to the object on the stack to represent the value of the generation (of course, the value is assigned by the system, and it will be allocated according to the type and scope of the object ), delete some new objects (such as local variables) as soon as possible without disturbing some old objects (such as application objects ). That is to say, an object may be placed in the first generation. This is acceptable, not from the first generation.
The. NET platform provides a class type named System. GC. It is programmed to use a static member set to interact with GC, so that you can easily view the managed heap situation and garbage collection information. For example, count the number of garbage collections performed by a generation, the current generation of an object, the amount of memory allocated to the managed heap, and the number of generations supported by the system. One of the most important is the GC. Collect method. In some rare cases, it may be advantageous to use this method to forcibly recycle garbage by programming:
The application will enter a piece of code, which does not want to be interrupted by possible garbage collection;
The application has just allocated many objects and wants to delete as much memory as possible.
In this case, you need to explicitly trigger a garbage collection. GC is always called when manual garbage collection is forced. waitForPendingFinalizers (), so that you can wait for a moment. It is determined that all the final objects must be cleaned up before the program continues. At the underlying layer, WaitForPendingFinalizers suspends the calling thread during the recycle process. This is a good thing to ensure that the Code does not call the method of the currently destroyed object.
GC. collect ();/* You can also add the first numeric parameter to specify whether to forcibly recycle a generation or add the second parameter, used to adjust how the Runtime Library enforces garbage collection */
GC. WaitForPendingFinalizers ();
Q: What if I don't call GC. WaitForPendingFinalizers?
It should be noted that, even if only one display request is made for garbage collection, CLR executes multiple garbage collection operations in the background.
8.5Construct an object that can be terminated (rule 3)
In chapter 6, we know that the Object class has a Finalize method, which does nothing by default. This method is a protection level, so it is impossible to use the vertex operator to get the class instance (Object) directly call the Finalize method of an object, that isNot Manual. On the contrary, only one natural garbage collection or passGC. CollectIn the process of forcible recovery, or when the application domain of the application is detached from the memory,When this object is deleted from the memory, GC willAutomaticCall this method (if this method is supported ). That is, the CLR automatically calls the terminator (destructor) of each terminable object created in its lifecycle ).
In addition, the Finalize rewrite of the value type is invalid because the value type itself is not hosted in the heap and cannot be garbage collected.
Most C # classes do not require explicit logic cleaning or custom Terminators (destructor), because if hosted objects are used, everything will eventually be recycled. Only when using unmanaged resources can a class be designed to clean itself after use. This is the third principle.
Rule 3: To clear unmanaged resources, you must override the System. Object. Finalize () method to automatically call the method when garbage collection occurs. However, it is strange that this rewrite does not use the override keyword (protected override void finalize), which leads to compilation errors. C # provides the Destructor syntax to achieve the same effect. The Destructor and constructor are similar. They must have the same name as the class, but cannot have parameters or return types. Only one class has one destructor in the following format (note the twists and turns ):
~ Myresourcewarpper ()
{
// Clear the unmanaged Resources
}
In fact, CLR will still convert it to the Finalize method in the IL intermediate language, and add the necessary error detection code (try/catch ).
Some details: the Finalize method should be kept in mind to ensure that. NET can be cleared during garbage collection.UnmanagedResource. If you create a type that does not use an unmanaged entity, the termination is useless. In fact, if possible, you should avoid providing the Finalize method (destructor) when designing the class, because it takes time to end.
The entire process is: when an object is allocated in the managed heap, the runtime automatically determines whether the object provides a custom Finalize method. If yes, the object is marked as final, at the same time, a pointer pointing to this object is saved in the internal queue named the end queue. The end queue is a table maintained by GC, it points to each object that must be terminated before being deleted from the stack.
When GC determines that an object is released from memory, it checks each item in the end queue and copies the object from the stack to another managed structure called the endable table. At this time,NextAnother thread is generated during garbage collection, and the Finalize method is called for each object in the reachable table. Therefore, at least two garbage collection is required to terminate an object.
In short, although the end of an object can ensure that the object can clear unmanaged resources, it is still uncertain in nature, and because of additional background processing, the speed becomes quite slow.
Question: "If you have created a type that does not use an unmanaged entity, the end is useless?
8.6Construct manageable objects (rule 4)
As mentioned above, the terminator can be used to clear unmanaged resources during garbage collection. However, because many unmanaged resources are very valuable, they should be cleared as quickly as possible, rather than relying on garbage collection. Besides rewritingThe Finalize class can also implement the IDisposable interface. If this interface is provided, it is assumed that when the object user no longer uses this object, it will be before this reference leaves the scope.ManualCall the Dispose method. In this way, you can execute any necessary unmanaged resource cleanup without any performance loss caused by placing the object on the end queue, and do not have to wait for the end logic of the garbage GC trigger class. This is Rule 4.
Note that both the value type and reference type can call the Dispose method, because both can implement the IDisposable interface.
Public class myresourcewarpper: IDisposable
{
Public void Dispose ()
{
// Clear unmanaged Resources
// Discard other disposal objects contained
}
}
The Dispose method is not only responsible for releasing an unmanaged resource of an object, but also calling the Dispose method for any disposal objects it contains. Unlike Finalize, the Dispose method is safe to communicate with other hosted objects. GC does not support the IDisposable interface, so it will never automatically call the Dispose method. Obviously, the Dispose method will occur in a manual call before GC, and will not worry that the resources to be disposed of have been recycled and errors will occur. However, this also has a drawback: The Dispose method is implemented to release unmanaged resources.Forgot to manually call, The unmanaged resources may always be stored in the memory.
To determine whether a type supports the IDisposable interface, you can view the SDK, or use the is or as keyword to determine whether the interface is supported.
Similar to Finalize, many of the accumulate libraries implement IDisposable classes and provide aliases for Dispose methods, such as fs. colse () is equivalent to fs. dispose (), but the latter is always correct.
To ensure that the Dispose method is manually called and each type of disposal can be explicitly packaged in try/catch blocks, it is obviously troublesome. Therefore, C # provides special syntax, the using keyword is used to automatically call the Dispose method, extend the Dispose method in IL, and add the try/catch logic.
Using (myresourcewarpper rw = new myresourcewarpper ())
{
// Use the rw object
}
This syntax structure ensures that after exiting the using block, the object being used (rw) willAutomaticCall the Dispose method. It can also be declared in the using scope.Multiple objects of the same type(Separated by commas), the compiler inserts code to call the Dispose method of each object.
8.7Build types that can be terminated and disposed
The preceding describes two ways to release unmanaged resources. On the one hand, it can be rewritten.The Finalize method is automatically called when the memory is recycled. On the other hand, it implements the Dispose method, allowing users to manually call and release unmanaged resources. The two technologies have their own characteristics and disadvantages,Put the two methods into the same classIs a good method.
In this way, if you remember to call the Dispose method, you can useGC. SuppressFinalize() Method to notify GC to skip the Finalize process (call the Finalize method). If the user forgets to call the Dispose method, the final object will be released by GC by calling the Finalize method. In short, the internal unmanaged resources of the object are released in one of these ways.
The formal disposal method Microsoft provides a somewhat rigid disposal model (MyResourceWrapper version) that balances robustness, maintainability, and performance. It ensures that the Finalize method does not attempt to Dispose of any managed object, and the Dispose method should do so, and that the object user can safely call the Dispose method multiple times without making any error, the code to be called by both methods is also concise by assisting private functions.