In. in. net, CLR manages allocated objects through garbage collection. C # Programmers never directly delete a managed object from the memory (C # language does not have the delete keyword ).. Net objects are allocated to a memory area called managed heap, where they will be automatically destroyed by GC at "some time in the future. This article describes the object lifecycle, garbage collection, and security types that can be terminated and disposed of in C.
Object Lifecycle
Class is just a blueprint to describe what this type of instance looks like in memory. After the class is defined, you can use the new keyword to create any number of objects, but the New Keyword returns a reference pointing 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. By default, a value assignment of a reference type will generate a new reference to the same object on the stack.
Describes the relationships between classes, objects, and references:
When the C # compiler encounters the New Keyword, it will add a rule to the implementation of the method. The core tasks of the pencil newobj command are as follows:
A. Calculate the total memory required for the allocation object (including the necessary memory required for the type of member variables and the type of base class );
B. Check the managed heap to ensure that there is sufficient space for the objects to be allocated. If the space is sufficient, call the Type constructor and return the reference of the new object in the memory to the caller;
C. Move the pointer to the next object (pointer to the allocated new object) to the next available location on the managed stack before returning the reference to the caller.
Shows the basic process:
When processing the newobj command, if the CLR determines that the managed heap does not have enough space to allocate the requested type, it will execute a garbage collection to try to release the memory.
After an object is created, for value-type objects, when they have a defined scope (for example, temporary variables in a method, after the method is returned ), objects of this type will die out. For Objects of the reference type, they are allocated to the managed stack, and these objects are kept in the memory. the. NET Garbage Collector destroys them.
Garbage Collection
As mentioned above, after an object of the reference type is created by new, the garbage collector will destroy it only when it is no longer needed. The problem is how does the Garbage Collector determine when an object is no longer needed? Simply put, when an object is inaccessible from any part of the code base (that is, the object has no root), the garbage collector marks it as garbage and becomes a candidate target for garbage collection.
In order to optimize the process of CLR searching for inaccessible objects, every object on the stack is specified as a "Generation". The idea of generation is simple: the longer the object will exist on the stack, it should be retained (for example, the object implementing the main method and the application object). On the contrary, objects placed on the stack recently may not be accessible soon (for example, objects created in the method scope, local variable ). Based on this, the objects on each stack belong to one of the following generations:
Generation 1: never marked as recycled new objects
1st: the object that was not recycled in the previous garbage collection (that is, it was marked as a recycle object, but it was not recycled because it had enough heap space)
Generation 1: objects not recycled after more than one garbage collection
During garbage collection, the garbage collector first processes 0th generation objects. If it is not enough to process 1st generation or even 2nd generation objects, the remaining 0th generation objects will be upgraded to 1st generation objects, and so on, the maximum value is 2nd generations.
When garbage collection occurs, the garbage collector temporarily suspends all threads that are active in the current process to ensure that the application will not access the heap during the collection process. Once the garbage collection cycle is completed, the suspended threads are allowed to continue their work.
Build terminable types
In the system. Object Class, the finalize () virtual method is defined to ensure that. Net objects can be cleared during garbage collection Unless managed resources are managed. Unmanaged resources (such as original file handles and database connections) are obtained through the use of the pinvoke (platform call) Service to directly call the operating system APIs or through complex com interactions. Obviously, the default implementation of this method is nothing.
Due to garbage collection, most C # classes do not need to overwrite the finalize () method to explicitly specify the clearing logic. All Managed Objects will eventually be garbage collection. You only need to customize the clearing logic when using unmanaged resources. The finalize () method is protected, so it is impossible to directly call the finalize () method of an object. The Garbage Collector automatically calls the finalize () method of the object before the object is recycled from the memory () for more information, see the description.
Note: rewriting finalize () on the structure type is invalid because the structure is a value type and they are never allocated to the stack.
It is strange to override finalize () in C #, and it cannot be done using the expected override Keyword:
- Public class myresourcewrapper
- {
- // Compiler error!
- Protected
Override void finalize (){}
- }
To override the finalize () method, you can use the following destructor syntax.
- Class myresourcewrapper
- {
- ~ Myresourcewrapper ()
- {
- // Clear the unmanaged resources here, and only test the resources here.
- Console. Beep ()
- }
- }
This alternative form is used because when the C # compiler processes an analysis function, it automatically adds the necessary error detection code to the Finalize method to ensure the finalize () of the base class () methods are always executed. If you use ildasm.exe to view the C # destructor, you will see:
- . Method family hidebysig virtual instance
Void
- Finalize () cel managed
- {
- // Code size 13 (0xd)
- . Maxstack 1
- . Try
- {
- Il_0000: LDC. I4 0x4e20
- Il_0005: LDC. I4 0x3e8
- Il_000a: Call
- Void [mscorlib] system. Console: beep (int32, int32)
- Il_000f: NOP
- Il_0010: NOP
- Il_0011: Leave. s il_001b
- } // End. Try
- Finally
- {
- Il_0013: ldarg.0
- Il_0014:
- Call instance void [mscorlib] system. Object: Finalize ()
- Il_0019: NOP
- Il_001a: endfinally
- } // End Handler
- Il_001b: NOP
- Il_001c: Ret
- } // End of method myresourcewrapper: Finalize
Termination is useless for types that do not use unmanaged resources. In fact, if possible, you should avoid providing the finalize () method when designing the type, because the termination takes time.
When an object is allocated on the managed stack, 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 (end Queue) maintained by the garbage collector. When the Garbage Collector determines to release an object, it checks every item in the end queue and copies the object from the stack to another managed structure endable table. The next garbage collection will generate another thread and call the finalize () method for each object in the reachable table. Therefore, to truly end an object, at least two garbage collections are required. All in all, although the end of an object can ensure that the object can clear unmanaged resources, it is still inherently uncertain, and the second phase will become quite slow due to additional background processing.
Build disposal types
Many unmanaged resources are very valuable, so they should be cleared as quickly as possible, although they can be cleared during garbage collection through the finalize () method, however, this will result in performance loss when the object is placed on the end queue, and you must wait for the garbage collector to trigger the end logic of the class. Therefore, another technology is required to process object cleanup-implement the idisposable interface, which defines a method named dispose, you can customize the necessary object clearing logic in this method.
The implementation of the idisposable interface is to assume that when the object user no longer uses this object, it will manually call dispose () before the object reference leaves the scope, so that the object can be cleaned up by unmanaged resources. Unlike the finalize () method, the dispose method is safe to communicate with other hosted objects, because the garbage collector does not support the idisposable interface and will never call dispose (), therefore, when the object user calls this method, the object is still on the managed stack and can access all other objects allocated on the stack.
Note: different from overwriting finalize (), the structure and class type support idisposable.
When processing a hosted object that implements idisposable, to ensure that the type of dispose () method is also called when an exception occurs during running, you usually need to package each disposal type in try/catch/finally blocks. C # provides the following special Syntax:
- Static void main ()
- {
- // When you exit the using scope, the system automatically calls dispose ()
- Using (myresourcewrapper RW =
New myresourcewrapper ())
- {
- // Use the RW object
- }
- }
- // If you use ildasm.exe to view the pencil code of the main () method, you will find that the using syntax is indeed called using dispose ()
- // Use the extended try/finally logic:
- . Method private hidebysig
Static void main (string [] ARGs) cel managed
- {
- ...
- . Try
- {
- ...
- } // End. Try
- Finally
- {
- ...
- Il_0012: callvirt instance void
- Simplefinalize. myresourcewrapper: dispose ()
- } // End Handler
- ...
- } // End of method program: Main
Build terminable disposal type
There are two methods to construct classes that can clean up internal unmanaged resources. One can override the finalize () method, and the other can implement idisposable. The former can be assured, because the object can be cleared without the user's involvement in garbage collection. The latter provides the object user with a method that can be cleared once the object is used up, however, if you forget to call dispose (), the unmanaged resources may remain in the memory forever.
At the same time, it is feasible to use the above two technologies to obtain the benefits of the two models. If the Object User calls dispose (), it can call GC. suppressfinalize () notifies the Garbage Collector to skip the termination process. If you forget to call dispose (), the object will be terminated. This method does work well, but there are also minor defects. First, the finalize () and dispose () methods both have the same code to clear unmanaged resources, define a private helper function for two methods to call. In addition, make sure that the finalize () method does not attempt to dispose of any managed objects, and the dispose () method should. Finally, make sure that the object user can safely call dispose () multiple times without errors.
To achieve this design, Microsoft defined a formal terminable and manageable model that balances robustness, maintainability, and performance. The following is an example of this mode:
- Public class myresourcewrapper: idisposable
- {
- // Used to determine if dispose () has already been called.
- // Used to determine whether dispose () has been called
- Private bool disposed =
False;
- Public void dispose ()
- {
- // Call our helper method. Call the auxiliary Method
- // Specifying "true" signifies that
- // The Object User triggered the clean up.
- // Specify true to indicate that the Object User triggers the cleaning process
- Dispose (true );
- // Now suppress finialization.
- // Skip the end now
- GC. suppressfinalize (this );
- }
- // Protected void virtual dispose (bool disposing)
- // You can also define the signature of this method.
- Private
Void dispose (bool disposing)
- {
- // Be sure we have not already been disposed!
- // Make sure that it has not been disposed
- If (! This. disposed)
- {
- // If disposing equals true, dispose all
- // Managed resources. If disposing is equal to true, dispose of all managed resources
- If (disposing)
- {
- // Dispose managed resources. Dispose of managed resources
- }
- // Clean up unmanaged resources here. Here, the unmanaged resources are cleared.
- }
- Disposed = true;
- }
- ~ Myresourcewrapper ()
- {
- // Call our helper method. Call the auxiliary Method
- // Specifying "false" signifies that
- // The GC triggered the clean up.
- // If it is set to false, GC triggers the cleaning process.
- Dispose (false );
- }
- }
Summary
At this point, the process description of managing objects through the garbage collector by CLR ends, excluding some details, such as weak reference and object resurrection. The following rules can be summarized at the end of the full text:
Rule 1. Use the New Keyword to allocate an object to the managed stack, so you don't have to worry about it;
Rule 2. If the managed heap does not have enough memory to allocate the requested object, garbage collection will be performed;
Rule 3. the only reason for rewriting finalize () is that the C # class uses unmanaged resources through pinvoke or complex com interoperability tasks (typically through system. runtime. interopservices. marshal type );
Rule 4. If an object supports idisposable, always call dispose () for any directly created object (). It should be considered that if the class designer chooses to support the dispose () method, this type needs to be cleared;