[CLR via C #] 4. Interaction between type basics and types, objects, stacks, and heap Runtime

Source: Internet
Author: User
Tags class manager

 
 

Since all types are ultimately derived from System. Object, we can ensure that each Object of each type has a set of basic methods.

System. Object provides the following public instance methods.

Equals (Object) Determines whether the specified object is equal to the current object. If the two objects have the same value, true is returned.
GetHashCode The returned object is worth a hash code. If an object of A type is used as a key in a hash table set, this method should be overwritten. The method should provide a good distribution for different objects.
ToString By default, this method returns the full name of the type (this. GetType (). FullName ).
GetType Returns an instance of an object derived from Type, indicating the Type of the object that calls GetType. The returned Type can be used with the reflection class to obtain metadata related to the object Type.

  

  

MemberwiseClone This non-virtual method can create a new instance of the type and set the instance field of the object to be exactly the same as the instance field of this object. A reference to the new instance is returned.
Finalize After the Garbage Collector determines that the object should be collected as garbage, this virtual method will be called before the object's memory is actually recycled. The type of cleanup tasks that need to be executed before collection should be overwritten.

  

Employee e =  Employee();

The following is what the new operator does:

1) it calculates the number of bytes required for all instances defined in the type and all base types (until System. Object. Each object on the stack needs some additional overhead members --"Type object pointer(Type object pointer) "and"Synchronize block Indexes"(Sync block index ). These members are used by CLR to manage objects. The number of bytes of these additional members is the size of the incoming object.

2) It allocates bytes of the specified type from the managed heap to allocate the object memory. All allocated bytes are set to zero (0 ).

3) It initializes the "type Object Pointer" and "synchronization block Index" members of the object.

4) Call the instance constructor to pass in any real parameters specified in the new call (in this example"

After all new operations are performed, a reference of the newly created object is returned. In this example, this reference is saved to variable e, which has the Employee type.

Note"Type objectPointer ",Type objectNoType object/instanceThe two are different.

----------------------------------------------------------------------------------

One of the most important features of CLR is type security. During runtime, CLR always knows the type of an object. You can call the GetType method to obtain the object type.

CLR allows an object to be converted to its actual type or any base type.

C # conversion of an object to any of its types is not required using special syntax, because conversion to the base type is considered a safe implicit conversion. However, when converting an object to a derived class, C # requires developers to only perform display conversion, because such conversion may fail at runtime.

Public static void Main () {// you do not need to convert Object o = new Employee (); // You Need To forcibly convert the type into Employee e = (Employee) o ;}

Another way to convert data types in C # is to use the is operator. The is operator checks whether an object is compatible with the specified type and returns a Boolean value (true and false ). Note that the is operator does not return exception information.

The is operator is usually used as follows:

  if ( o is Employe ){       Employee e = (Employee) o;  }

In this Code, the CLR actually checks the object type twice. The is operator first checks whether o is compatible with the Employee type. If yes, the CLR will verify again whether o references an Employee. The enhanced security of CLR type checks will undoubtedly affect the performance.

C # provides the as operator to simplify the code writing and improve performance.

The as operator is usually used as follows:

Employee e = o as Employee; if (e! = Null) {// use e in if}
The as operator works in the same way as forced type conversion, but it does not throw an exception. If it cannot be converted, the result is null. Therefore, the correct method is to check whether the final generated reference is null. If you attempt to directly use the converted reference, an exception is thrown. ------------------------------------------------------------------------------ Namespace is used to logically group related types. developers can use namespaces to easily locate a type.

Namespaces and assemblies are not necessarily related, that is, they are not necessarily related.

----------------------------------------------------------------------------------

Now we will explain the connection between types, objects, thread stacks, and managed stacks at runtime. It also explains the differences between calling static methods, instance methods, and virtual methods.

Let's start with the thread stack.

1. Figure 4-2 shows a Windows Process with CLR loaded. There may be multiple threads in this process. When a thread is created, it is allocated to a 1 MB stack. The stack space is used to pass real parameters to the method and to call local variables defined inside the method. Figure 4-2 shows the stack memory of a thread (on the right ). Stack is built from high address to low address. As shown in the figure, the thread has executed some code. Now, it is assumed that the code that the thread starts to execute needs to call the M1 method.

  

2. There will be some "prelude" code in the most basic method, which is responsible for initializing the method before it starts to work. In addition, the "end" code is included, which is responsible for clearing the method after it is completed and then returning it to the caller. When the M1 method is executed, its "prelude" code allocates the memory of the local variable name on the thread stack, as shown in Figure 4-3.

  

3. Then, M1 calls the M2 method and uses the local variable name as a real parameter for transmission. This causes the address in the name local variable to be pushed to the stack (see Figure 4-4 ). Inside the M2 method, the parameter variable named s is used to identify the stack location (some CPU architectures transmit real parameters through registers to improve performance ). In addition, when a method is called, a "return address" is pushed into the stack. After the called method is completed, it should be returned to this position (see Figure 4-4 ).

  

4. When the M2 method starts execution, its "prelude" code is to allocate memory for the local variable length and tally in the thread stack. 4-5. Then, the code inside the M2 method starts to be executed. Finally, M2 reaches its return statement, causing the CPU instruction pointer to be set to the return address in the stack, and the stack frame of M2 is expanded to make it look similar to Figure 4-3. Then, M1 will continue to execute the code after the M2 call, And the M1 stack frame will accurately reflect the status required by M1. 5. Finally, M1 will return to its caller. Similarly, it is implemented by setting the CPU instruction pointer to the return address (this return address is not shown in the figure, but it should be above the name real parameter in the stack ), in addition, the stack frame of M1 is expanded to make it look similar to Figure 4-2. After the M1 method is called, the code after M1 will continue to be executed, and the stack frame of the method will accurately reflect the desired state. CLR operation Relationship 1. Assume that there are two classes defined:
internal class Employee {    public               int32         GetYearsEmployed()       { ... }    public    virtual    String        GenProgressReport()      { ... }    public    static     Employee      Lookup(String name)      { ... }     }internal sealed class Manager : Employee {      public    override   String         GenProgressReport()    { ... }}     

2. Our Windows process has been started, the CLR has been loaded to it, the managed heap has been initialized, and a thread has been created (along with its 1 MB stack space ). This thread has executed some code and is now calling the M3 method. Figure 4-6 shows the current situation. The Code contained in the M3 method demonstrates how the CLR works.

   

3. when the JIT compiler converts M3 IL code into a local CPU command, it will notice all types referenced internally by M3: Employee, Int32, Manager, and String (because "Joe "). At this time, the CLR should ensure that all the assemblies that define these types have been loaded. Then, using the metadata of these sets, CLR extracts information related to these types and creates some data structures to represent the types themselves. Figure 4-7 shows the data structure used for objects of the Employee and Manager types. Since this thread has executed some code before calling M3, it may be assumed that Int32 and String type objects have been created, so they are not shown in the figure. 4. Prerequisite: all objects on the stack contain two additional members: "type Object Pointer" and "synchronization block Index ". As shown in Figure 4-7, both the Employee and Manager objects have these two members. When defining a type, you can define static data fields within the type. The bytes that are supported for these static field data are allocated to the type object itself. Each type of object contains a method table. In the method table, each method defined in the type has a corresponding record item. Since the Employee has three methods, there are three record items, and the Manager has only one method, there is only one record item. 5. Now, when the CLR determines that all types of objects required by the method have been created and the M3 code has been compiled, the thread can start to execute the local code of M3. During code execution of M3, memory must be allocated for local variables in the thread stack, as shown in Figure 4-8. As part of the method's "prelude" code, CLR will customize to initialize all local variables to null or zero (0 ). 6. Then, M3 executes its code to construct a Manager object. This creates an instance of the Manager type (that is, the Manager object) in the managed heap ). See Figure 4-9. Like all objects, the Manager object also has a "type Object Pointer" and "synchronous block Index ". This Object also contains necessary bytes to accommodate all instance data fields defined by the Manager type and all instance fields defined by any base class (Employee and Object) of the Manager. When a new object is created on the stack at any time, CLR will automatically initialize the internal "type Object Pointer" for it to reference (or point) the type object corresponding to the object (in this example, the Manager type object ). In addition, CLR initializes "Synchronize block indexes" and sets all instance fields of the object to nll or zero (0 ), in the call Type constructor (it is essentially a method that may modify some instance fields ). The new operator returns the memory address of the Manager object, which is saved in variable e (e on the thread stack ). 7. The next line of M3 code calls the static Lookup method of Employee. When a static method is called, the CLR locates the type object corresponding to the type that defines the static method. Then, the JIT compiler searches for the record items corresponding to the called method in the method table of the type object, compiles the method with JIT (if needed), and then calls the Code Compiled by JIT. In this example, it is assumed that the Lookup method of Enployee needs to query the Joe in the data. In addition, assuming that Joe is a Manager in the database, the Lookup method constructs a new Manager object on the heap, initializes it with the information of Joe, and then returns the address of the object. This address is stored in the local variable e. As shown in Figure 4-10. It is worth noting that e no longer references the first Manager object. In fact, because no variable references the first Manager object, it is the main target for future garbage collection. 8. The next line of M3 calls the non-virtual instance method GetYearsEmployed of Employee. The JIT compiler will find the type object (Employee type object) corresponding to the "Emplyee" type of the variable (e) that is called ). In this example, variable e is defined as an Employee. If the Employee type does not define this method, the JIT compiler will trace the class hierarchy (until the Object) and search for this method in each type along the way. The reason for this backtracking is that each type object has a field that references its base type, but it is not shown in the figure. Then, the JIT compiler searches for record items that reference the called method in the method table of the type object, compiles the method with JIT (if needed), and then calls the call after JIT compilation. In this example, assume that the GetYearsEmployed method of the Employee returns 5 ,. This integer is saved in the local variable year. As shown in Figure 4-11. 9. The next line of M3 code calls the real-world sample method GenProgressReport of Empolyee. When calling a real-world sample method, the JIT compiler must generate some additional code in the method. The code is executed every time the method is called. The code first checks the variables that make the call, and then follows the address to the object that sends the call. In this example, variable e references a Manager object that represents "Joe. Then, the code checks the "type Object Pointer" member in the object, which points to the actual type of the object. Then, the Code searches for record items that reference the called method in the method table of the type object, compiles the method with JIT (if needed), and then calls the Code Compiled by JIT. In this example, because e references a Manager object, the GenProgressReport Implementation of Manager is called. As shown in Figure 4-12. Conclusion: both the Employee and Manager objects contain "type Object Pointer" members. This is because the type object is essentially an object. When creating a type object, CLR must initialize these members. What is initialization? When CLR starts running in a process, it immediately creates a special Type object for the System. Type defined in MSCOrLib. dll. The Employee and Manager Type objects are "instances" of this Type. Therefore, their Type object pointer members are initialized to reference System. Type objects. 4-13. Of course, the System. Type object itself is also an object, and there is also a member of "Type Object Pointer. So what is this pointer pointing? It points to itself, because the System. Type object itself is an "instance" of a Type object ". Now we understand the entire type system of CLR and how it works. The GetType method of System. Object returns the address stored in the "type Object Pointer" member of the specified Object. That is to say, the GetType method returns a pointer to the object type object. In this way, you can determine the real type of any object in the system (including the type object itself.

GetType

Related Article

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.