Content introduction
•Overview
•What happens when you declare a variable? what happened?
•Heap and stack
•Value types and reference types
•Which are value types and which are reference types?
•Boxing and unboxing
•Performance issues of boxing and unboxing
1. Overview
This article will explain six Important concepts: heap, stack, value types, reference types, boxing and unboxing. This article will start by explaining the changes that occur within the system when you define a variable, and then shift the focus to the storage duo: the heap and the stack. Later, we will explore value types and reference types and explain the important basics about these two types.
This article will use a simple code to show the performance impact caused by the boxing and unboxing process. Please read it carefully.
2. What happens behind the scenes when you declare a variable?
When you define a variable in a .NET application, some memory block is allocated for it in RAM. This memory contains three things: the name of the variable, the data type of the variable, and the value of the variable.
The above is a brief explanation of what happens in memory, but what type of memory your variables will be allocated to depends on the data type. There are two types of allocable memory in .NET: stack and heap. In the next few sections, we will try to understand these two types of storage in detail.
#3. Storage duo: heap and stack
public void Method1() { // Line 1 int i=4; // Line 2 int y=2; //Line 3 class1 cls1 = new class1(); }
•Another important point to note here is that the reference pointer of the object is allocated on the stack. For example: the declaration statement Class1 cls1; actually does not allocate memory for the instance of Class1, it just creates a reference pointer for the variable cls1 on the stack (and sets its default position to null). Only when it encounters the new keyword will it allocate memory for the object on the heap.
•When leaving this Method1 method (the fun): Now the execution control statement begins to leave the method body. At this time, all the memory space allocated for variables on the stack will be cleared. In other words, in the above example, all variables related to the int type will be popped from the stack one by one in a "LIFO" last-in-first-out manner.
•It should be noted that it will not release the memory blocks in the heap at this time. The memory blocks in the heap will be cleaned up later by the garbage collector.
#Now many of our developer friends must be curious why there are two different types of storage? Why can't we allocate all memory blocks to only one type of storage?
If you look carefully enough, primitive data types are not complex, they just hold values like ‘int i = 0’. Object data types are more complicated; they reference other objects or other primitive data types. In other words, they hold references to multiple other values and these values must be stored in memory one by one. Object types require dynamic memory while primitive types require static memory. If the requirement is dynamic memory, then it will allocate memory for it on the heap, otherwise, it will allocate it on the stack.
4. Value types and reference types
Now that we have understood the concepts of stack and heap, it is time to understand value types and references The concept of type. Value types keep both data and memory in the same location, whereas a reference type has a pointer to the actual memory area.
Through the figure below, we can see an integer data type named i, and its value is assigned to another integer data type named j. Their values are stored on the stack.
When we assign a value of type int to another value of type int, it actually creates a completely different copy. In other words, if you change the value of one, the other will not change. Therefore, these kinds of data types are called "value types".
When we create an object and assign this object to another object, they both point to the same area of memory as shown in the code snippet below. Therefore, when we assign obj to obj1, they both point to the same area in the heap. In other words, if we change any one of them at this time, the other will be affected, which also explains why they are called "reference types".
5. Which are value types and which are reference types?
In .NET, whether a variable is stored on the stack or in the heap depends entirely on the data type it belongs to. For example: 'String' or 'Object' are reference types, while other .NET primitive data types will be allocated on the stack. The figure below shows in detail which of the .NET preset types are value types and which are reference types.
##
6. Packing and unboxing
Now, you already have a lot of theoretical foundation. Now, it's time to understand how the above knowledge is used in actual programming. One of the biggest implications in applications is to understand the performance consumption issues that occur when data is moved from the stack to the heap, and vice versa.
Consider the following code snippet. When we convert a value type to a reference type, the data will be moved from the stack to the heap. Conversely, when we convert a reference type to a value type, the data is also moved from the heap to the stack.
Whether it is moving from the stack to the heap or from the heap to the stack, it will inevitably have some impact on system performance.
As a result, two new terms emerged: when the data is converted from a value type to a reference type, the process is called "boxing", and the process of converting from a reference type to a value type is called "boxing". Become "unboxed".
#
If you compile the above code and view it in ILDASM (an IL decompilation tool), you will find that in the IL code , what boxing and unboxing looks like. The figure below shows the IL code generated after the sample code is compiled.
#
7. Performance issues of boxing and unboxing
In order to understand the end What kind of performance impact will boxing and unboxing have? We run the two function methods shown in the figure below 10,000 times in a loop. The first method has boxing operations, the other does not. We use a Stopwatch object to monitor the consumption of time.
The method with boxing operation took 3542 milliseconds to complete, while the method without boxing operation only took 2477 milliseconds, a difference of more than 1 second. Moreover, this value will also increase as the number of cycles increases. In other words, we should try to avoid boxing and unboxing operations. In a project, if you need to box and box, carefully consider whether it is an absolutely essential operation, if not, then try not to use it.
#
#Although the above code snippet does not Shows unboxing, but the effect applies to unboxing as well. You can write code to implement unboxing and test its time consumption through Stopwatch. The above are the six important concepts in .NET: stack, heap, value type, reference type, boxing and unboxing. For more related content, please pay attention to the PHP Chinese website (www.php.cn )!