1,
Packing and unpacking are an abstract concept.
2,
Packing is Value Type Convert Reference Type . Reference Type Convert Value Type
You can use the packing and unpacking functions to allow Value Type Any value Object Type And link the value type with the reference type.
For example:
Int val = 100;
Object OBJ = val;
Console. writeline ("object value = {0}", OBJ );
This is a packing process. Value Type Convert Reference Type Process
Int val = 100;
Object OBJ = val;
Int num = (INT) OBJ;
Console. writeline ("Num: {0}", num );
This is a box-breaking process. It is the process of converting the value type to the reference type and then converting the reference type to the value type.
Note: Only objects that have been packed in boxes can be split.
3,
In. net, data types are dividedValue TypeAndReference(Not the same as the pointer of C ++)TypeCorrespondingly, memory allocation is divided into two methods: Stack and heap. Note: it is a managed heap.
The value type is only allocated in the stack.
Allocate memory and managed heap for reference types.
The managed heap corresponds to garbage collection.
4: What is packing/unpacking?
Packing: used to store value types in the garbage collection heap. Packing is an implicit conversion between the value type and the object type or any interface type implemented by this value type.
Unpacking: the explicit conversion from the object type to the value type or from the interface type to the value type of the interface.
5: Why is packing required? (Why convert the value type to the reference type ?)
One of the most common scenarios is to call a method containing parameters of the object type. This object can support any type for general purpose. You need to pack a value type (such as int32.
Another method is to define an element type as an object to ensure the universality of a non-generic container. Therefore, to add the value type data to the container, You need to pack the data.
6: binning/unboxing internal operations.
Packing:
Assign an object instance to the heap and copy the value to the new object. Step by step.
Step 1: allocate new managed heap memory (size: Value Type instance size plus a method table pointer and a syncblockindex ).
Step 2: copy the instance field of the value type to the newly allocated memory.
Step 3: return the address of the newly allocated object in the managed heap. This address is a reference to the object.
Someone understands this: If int32 is packed, the returned address points to an int32. I don't think it can be understood in this way, but this is indeed a problem. It is incomplete, and int32 does not speak about its essence (in the managed heap ).
Unpack:
Check the object instance to make sure it is a boxed value of the given value type. Copy the value from the instance to the value type variable.
In the book, unpacking only gets the pointer to the value type in the referenced object, and copying the content is triggered by the value assignment statement. I think it doesn't matter. The most important thing is to check the nature of the object instance, and The binning and packing types must match. On this point, on the Il layer, I cannot see the principle. I guess, maybe a method like GetType is called to retrieve the type for matching (because strict matching is required ).
7. Effect of packing/unpacking on execution efficiency
Obviously, from the principle, we can see that during packing, a brand new reference object is generated, which may result in time loss, that is, reduced efficiency.
What should we do?
First, try to avoid packing.
For example, you can avoid both of the above two cases. In the first case, you can avoid this by using the overload function. In the second case, you can avoid using generics.
Of course, everything cannot be absolute. Suppose what you want to transformCodeThird-partyProgramSet, you cannot change, then you can only pack.
For the optimization of packing/unpacking code, since C # is implicit in both packing and unpacking, the fundamental method is to analyze the code, the most direct method of analysis is to understand how to view the decompiled il code. For example, there may be extra packing in the loop body. You can simply use the advance packing method for optimization.
8: better understanding of packing/unpacking
Packing/unpacking is not as simple and clear as mentioned above. For example, if a reference object is changed during packing, a method table pointer will be added. What is the use of this method?
We can further explore through examples.
For example.
Struct a: icloneable
{
Public int32 X;
Public override string tostring (){
Return string. Format ("{0}", X );
}
Public object clone (){
Return memberwiseclone ();
}
}
Static void main ()
{
A;
A. X = 100;
Console. writeline (A. tostring ());
Console. writeline (A. GetType ());
A a2 = (a) A. Clone ();
Icloneable c = a2;
Ojbect o = C. Clone ();
}
5.0: A. tostring (). The compiler finds that a overrides the tostring method and directly calls the tostring command. Because a is a value type, the compiler does not show polymorphism. Therefore, it is called directly without packing. (Note: tostring is the method of the base class system. valuetype of)
5.1:. getType (), GetType is inherited from system. to call the valuetype method, a method table pointer is required. Therefore, a is boxed to generate a method table pointer and call the System of the base class. valuetype. (ADD, all value types are inherited from system. valuetype ).
5.2: A. Clone (). Because a implements the clone method, no packing is required.
5.3: icloneable Transformation: When A2 is converted to the interface type, it must be packed because the interface is a reference type.
5.4: C. Clone (). You do not need to pack it. Call the packed objects in the previous step in the managed stack.
Note: in fact, the above is based on a fundamental principle. Because ununboxed value types do not have method table pointers, you cannot use value types to call the inherited Virtual Methods on them. In addition, the interface type is a reference type. In my understanding, the table pointer of this method is similar to the virtual function table pointer of C ++. It is an important basis for implementing the polymorphism mechanism of referenced objects.
9: How to change packed objects
For packed objects, because the specified method cannot be called directly, you must unpack the object before calling the method. However, if you unpack the object again, a new stack instance is generated and the boxed object cannot be modified. A little dizzy. I feel like I'm talking about tongue twisters. For example: (append the change method in the above example)
Public void change (int32 X ){
This. x = X;
}
Call:
A A = new ();
A. X = 100;
Object o = A; // bind it to O. below, you want to change the O value.
(A) O). Change (200); // has it been changed? Not changed.
The reason for not changing is that O generates a temporary stack instance a when unpacking. Therefore, the change is based on temporary A and has not been changed to the packing object.
(Appendix: In managed C ++, you can directly change the instance reference obtained in step 1 when directly taking and disassembling the box, but C # does not work .)
So how should we be good?
Well, the same effect can be achieved through the interface method.
The implementation is as follows:
Interface ichange {
Void change (int32 X );
}
Struct a: ichange {
...
}
Call:
(Ichange) O). Change (200); // has it been changed? Get rid of it.
Why can I change it now?
When converting o to ichange, it will not be packed again here. Of course, it will not be split, because o is already of the reference type, and because it is of the ichange type, you can directly call change, therefore, the fields in the boxed object are changed to achieve the expected effect.
10,--------------------------
To convert a value type to a reference type, you need to perform the boxing operation ):
1. First, allocate memory for the newly generated reference object from the managed heap.
2. copy the data of the value type to the allocated memory.
3. Return the address of the newly allocated object in the managed heap.
It can be seen that the memory allocation and data copy operations affect the performance during a packing operation.
To convert a referenced inner type to a value inner type, unboxing is required ):
1. First, obtain the address of the field of the value type in the managed heap. This step is a strict unpacking.
2. Copy the value in the reference object to the value type instance on the thread stack.
After these two steps, it can be considered that the same boxing is an inverse operation. In a strict sense, unpacking does not affect the performance, but the subsequent data copying operations will affect the performance as in boxing operations.
11, -----------------------
all types of net are composed of the base class system. object Inheritance, including the most common basic types: int, byte, short, bool, etc., that is, all things are objects. If you declare that these types of memory are allocated in heap, it will cause extremely low efficiency! (The medium reason and the difference between stack and stack will be discussed separately in another article !)
How does. net solve this problem? By dividing a type into a value type ( value ) and a reference type ( regerencetype ). The value types defined in C # include the original type ( sbyte, byte, short, ushort, Int, uint, long, ulong, Char, float, double, bool, decimal ), enumeration ( Enum ), structure ( struct ), reference types include: Class, array, interface, Delegate, String, etc.
the value type is used to allocate memory in the stack. It is initialized at the same time as the Declaration to ensure that the data is not null.
the reference type is used to allocate memory in the heap, the initialization is null. The reference type requires garbage collection to recycle the memory. If the value type is not used, the system will automatically release it if it is out of scope!
The following is the definition of packing and unpacking!
packing is to implicitly convert a value type to a reference object. For example:
int I = 0;
lead E. object OBJ = I;
this process is packed! It is to pack I!
unpacking is to convert a referenced object into any value type! For example:
int I = 0;
system. object OBJ = I;
Int J = (INT) OBJ;
the first two sentences in this process are packing I, the next sentence is to unpack the OBJ!