Although we don't need to worry about memory management and garbage collection (garbage Collection) under the. NET Framework, we should still be aware of them to optimize our applications. At the same time, there is a need for some basic knowledge of memory management mechanisms, which can help explain the behavior of variables in our daily program writing. In this article I will explain the basics of stacks and heaps, the types of variables, and why some variables work in their own way.
In the. NET framework environment, when our code executes, there are two places in memory to store the code. If you never know, let me introduce you to stacks and heaps. Stacks and heaps are used to help us run code that resides in machine memory and contains all the information needed to execute the code.
* Stack vs Heap: What's the difference?
The stack is responsible for saving our code to execute (or call) the path, while the heap is responsible for saving the object (or data, and then talking about a lot of questions about the heap).
You can think of stacks as a stack of boxes stacked from top to bottom. When each method is called, we record what is going to happen in the application in a box at the top of the stack, and we can only use that box at the top of the stack at a time. When the box on top of our stack is used, or after the method has been executed, we will throw the box away and continue using the new box on top of the stack. The heap works similarly, but most of the time the heap is used to save information rather than save the execution path, so the heap can be accessed at any time. There are no access restrictions on the heap compared to stacks, and heaps are like old clothes on the bed, and we don't take the time to sort it out because we can always find the clothes we need, and the stacks are like the boxes stacked in the locker, and we can only start with the top-level boxes until we find the right one.
[Heapvsstack1.gif]
Slices are not real-world representations of memory, but can help us differentiate stacks and heaps.
The stack is self-sustaining, which means that the memory automatically maintains the stack, and when the stack top box is no longer used, it will be thrown. On the contrary, the heap needs to consider garbage collection, garbage collection is used to keep the heap neat, no one wants to see all the clothes around, it stinks!
* What's in stacks and heaps?
When our code executes, there are four types of data in the stack and heap: The value type, the reference type (Reference type), the pointer (Pointer), the directive (instruction).
1. Value type:
In C #, all things declared as the following types are referred to as value types:
bool
Byte
Char
Decimal
Double
Enum
Float
Int
Long
SByte
Short
struct
UInt
ULong
UShort
2. Reference type:
All things that are declared as the following types are referred to as reference types:
Class
Interface
Delegate
Object
String
3. Pointers:
The third type that is placed in a memory management scenario is a type reference, which is usually a pointer. We do not display the use pointers, which are managed by the common language runtime (CLR). A pointer (or reference) is different from a reference type, because when we say that something is a reference type, it means that we are accessing it through pointers. The pointer is a piece of memory space, and it points to another memory space. Just like stacks and heaps, pointers also take up memory space, but their values are either a memory address or empty.
[Heapvsstack2.gif]
4. Directives:
In the following article you will see how the instructions work ...
* How to decide where to put it?
Here's a golden rule:
1. reference types are always placed in the heap. ( simple enough?)
2. value types and pointers are always placed where they are declared. ( this is a bit more complicated, and you need to know how the stack works before you can determine where it is declared.) )
As we mentioned earlier, the stack is the path that is responsible for saving our code when it executes (or calls). When our code starts calling a method, it places a coded instruction (in the method) onto the stack, followed by the parameters of the method, and the code executes to the variable position in the method that is "stacked" to the top of the stack. It is easy to understand through the following examples ...
Here is a method:
public int addfive (int pValue)
{
int result;
result = PValue + 5;
return result;
}
Now take a look at what's going on at the top of the stack, and remember that the top of the stack we're looking at actually has a lot of other stuff in it.
The first method (contains only the logical bytes that need to be executed, that is, the instruction that executes the method, not the data in the method body) into the stack, followed by the method's parameters into the stack. (We'll discuss more parameter passing later.)
[Heapvsstack3.gif]
Next, the control (that is, the thread that executes the method) is passed to the addfive () command on the stack,
[Heapvsstack4.gif]
When the method executes, we need to allocate some memory on the stack for the "result" variable.
[Heapvsstack5.gif]
The method finishes execution and our result is returned.
The method execution is completed, and the result of the method is returned.
[Heapvsstack6.gif]
By pointing the stack pointer to the available memory address used by the addfive () method, all memory used by the method on the stack is emptied, and the program automatically returns to the location of the original method call on the stack (not seen in this example).
[Heapvsstack7.gif]
In this example, our "result" variable is placed on the stack, in fact, when the value type data is declared in the method body, they are placed on the stack.
Value type data is also sometimes placed on the heap. Remember this rule--value types are always placed where they are declared. Well, if a value type data is declared outside the method body and exists in a reference type, it will be superseded by the reference type in the heap.
See another example:
If we have such a myint class (it is a reference type because it is a class type):
public class MyInt
{
public int myvalue;
}
Then execute the following method:
Public MyInt addfive (int pValue)
{
MyInt result = new MyInt ();
Result. myvalue = PValue + 5;
return result;
}
As mentioned earlier, the parameters of the method and method are placed on the stack, and then the control is passed to the addfive () command on the stack.
[Heapvsstack8.gif]
Then there are some interesting phenomena ...
Because "MyInt" is a reference type, it is placed on the heap and a pointer reference to the heap is generated on the stack.
[Heapvsstack9.gif]
After the Addfive () method is executed, we will empty ...
[Heapvsstack10.gif]
We will leave the lonely Myint object in the heap (there will be no pointers to the Myint object in the stack!)
[Heapvsstack11.gif]
This is where the garbage collector (hereafter referred to as GC) works. When our program reaches a specific memory threshold, we need more heap space when the GC starts to work. The GC stops all running threads, finds all the objects that exist in the heap that are no longer accessible to the main program, and deletes them. The GC then re-organizes all the remaining objects in the heap to save space and adjusts all the pointers associated with those objects in the stack and heap. You'll think that this process is very expensive, so you'll know why we need to focus so much on stacks and heaps, especially when it comes to writing high-performance code.
Ok... This is great, when how does it affect me?
Good question.
When we use reference types, we are actually dealing with pointers of that type, not the type itself. When we use value types, we are using the value type itself. Sounds confusing, doesn't it?
Again, the example is the best description.
If we perform the following methods:
public int returnvalue ()
{
int x = new int ();
x = 3;
int y = new int ();
y = x;
y = 4;
return x;
}
We're going to get a value of 3, it's simple, right?
If we first use the Myint class
public class MyInt
{
public int myvalue;
}
The following methods are then performed:
public int ReturnValue2 ()
{
MyInt x = new MyInt ();
X.myvalue = 3;
MyInt y = new MyInt ();
y = x;
Y.myvalue = 4;
return x.myvalue;
}
What will we get? ... 4!
Why? How can x.myvalue become 4? ... Look what we've done, and we'll know what's going on:
In the first example, everything goes as planned:
public int returnvalue ()
{
int x = 3;
int y = x;
y = 4;
return x;
}
[Heapvsstack12.gif]
In the second example, we did not get "3" because the variable "x" and "Y" both point to the same object in the heap.
public int ReturnValue2 ()
{
MyInt x;
x.myvalue = 3;
MyInt y;
y = x;
y.myvalue = 4;
return x.myvalue;
}
[Heapvsstack13.gif]
Hopefully, this will give you a better idea of the basic differences between value types and reference types in C #, and a basic understanding of when pointers and pointers are used.
Type examples:
Using System;
Using System.Collections;
Using System.Collections.Generic;
Using System.Linq;
Using System.Text;
Using System.Text.RegularExpressions;
Using System.Threading.Tasks;
Namespace ConsoleApplication5
{
Class Test
{
public string I;
}
Class Test2
{
public string I;
}
Class Program
{
static void Main (string[] args)
{
int i = 0;
String A;
String B;
string C;
A = "123";
b = "456";
c = "789";
Test T1 = new test ();
Test t2 = new Test ();
T1.I = "ABC";
t2.i = "Def";
Test2 tt1 = new Test2 ();
Test2 tt2 = new Test2 ();
tt1.i = "Applied Type";
tt2.i = "Applied type tests";
TT1 = TT2;
Console.WriteLine (a);//indicates that it is not a value type
Console.WriteLine (b);//indicates that it is not a value type
Console.WriteLine (c);//indicates that it is not a value type
Console.WriteLine (t1.i);//indicates that it is not a value type
Console.WriteLine (t2.i);//indicates that it is not a value type
Console.WriteLine (tt1.i);//indicates that it is not a value type
Console.WriteLine (tt2.i);//indicates that it is not a value type
if (I.gettype (). Isvaluetype)
{
Console.WriteLine ("I is a value type");
}
Else
{
Console.WriteLine ("I is a reference type");
}
if (A.gettype (). Isvaluetype)
{
Console.WriteLine ("A b c is a value type");
}
Else
{
Console.WriteLine ("A b c is a reference type");
}
if (t1. GetType (). Isvaluetype)
{
Console.WriteLine ("Text is a value type");
}
Else
{
Console.WriteLine ("Text is a reference type");
}
Console.WriteLine ();
Console.readkey ();
The results of the exercise
123
456
789
Abc
Def
Applied Type Tests
Application tests
I is a value type
ABC is a reference type
Text is a reference type
}
}
}
Detailed introduction to stacks and heaps