In C #, there are two main object types: reference type (heavyweight Object) and value type (lightweight object ).
Reference types are always allocated in the heap (unless the stackalloc keyword is used), and an additional indirect layer is given; that is, they need to be accessed by referencing their storage location. Since these types cannot be accessed directly, a variable of the reference type always saves the reference (or null) of the actual object rather than the object itself. Assuming that the reference type is allocated in the heap, you must ensure that each allocation request is correctly executed during runtime. Consider the following:CodeIt executes a successful allocation:
Matrix M = new matrix (100,100 );
The execution behind the scenes is that the CLR Memory Manager receives the allocation request and calculates the amount of memory required to store the object including the header and class variables. The memory manager then checks the available free space in the heap to check whether there is sufficient space for this allocation. If yes, the space required for the object is successfully allocated and the reference to its storage address is also returned. If there is not enough space to store objects, the garbage collector will be started to release some space and perform heap compression.
If the execution is successful, in order to keep subsequent garbage collection operations, the memory manager must take another important step before writing objects into the memory. This step involves generating a piece of code called the write barrier (the implementation details of the garbage collector are beyond the scope of this article ). On the contrary, when an object is written to the memory or when the object is referenced to another object in the memory (for example, an existing object points to a new object), a write barrier is generated during the runtime. One of the many complexities implemented by the garbage collector function is to remember the existence writability of these objects, so they will not be mistakenly collected during the collection process, although they are directed by another unrelated object. As you may guess, these write barriers incur a small amount of runtime overhead, so it is not ideal for scientific computing applications to create millions of objects during the runtime.
The value type is directly stored in the stack (although there are exceptions to this rule, I will talk about it soon ). The value type does not require an indirect layer, so the value type variables always save their actual values and cannot save the reference as other types (so they cannot be null ). The main advantage of using value types is that their distribution only produces a small runtime overhead. When allocating them, you simply add a stack pointer and do not need to be managed by the Memory Manager. These objects never call the garbage collection function. In addition, value types do not generate write barriers.
In C #, simple data types (INT, float, byte), enumeration types, and structure (struct) are value types. Although I have mentioned that the value type is directly stored in the stack, I didn't use "always", just as I did when talking about the reference type. The Value Type contained in the reference type is not stored in the stack, but in the heap. It is included in the reference type object. For example, look at the following code snippet:
Class Point
{
Private Double X, Y;
Public point (Double X, Double Y)
{
This. x = X;
This. Y = y;
}
}
An instance of this class occupies 24 bytes, of which 8 bytes are used for the object header, and the remaining 16 bytes are used for two double precision variables X and Y. At the same time, the reference type is included in the value type object (for example, the structure contains arrays), it does not cause the entire object to be allocated in the heap. Only the array is allocated in the heap, and the reference to this array is placed in the Structure Stored in the stack.
The value type is derived from system. valuetype, which is also derived from system. object. Because of this, value types have the same features as classes. Value types include Constructor (except non-parametric constructor), index indicator (Indexer), method, and overload operator. They can also implement interfaces. However, they cannot be inherited or inherited from their types. These objects are easy to become JIT optimization factors because they generate effective and high-performance code.
here is a warning: it is extremely easy to accidentally put the value type into an object, resulting in allocating it in the heap-as we all know, this is the boxing technology. Make sure that your code will not be boxed in unnecessary values, otherwise it will lose the initial performance. Another warning is that value-type arrays (such as double-precision or integer arrays) are stored in the heap instead of in the stack. Only the values that save the array references are stored in the stack. This is because all array types are implicitly derived from system. array, and they are all reference types.