栈、托管堆、值类型、引用类型的理解
1. 托管堆与堆栈
托管堆:初始化新进程时,运行时会为进程保留一个连续的地址空间区域,这个保留的地址空间称为托管堆。托管堆维护着一个指针,用它指向将在堆中分配的下一个对象的地址,最初,该指针设置为指向托管堆的基址。
托管代码:使用CLR的语言编译器开发的代码统称托管代码。
△值类型直接存储基值,引用类型存储对值的引用,值类型存储在堆栈上,引用类型存储在托管堆上,值类型→引用类型(装箱),引用类型→值类型(拆箱)
堆栈与托管堆的工作方式:
在C/C++中:
Stack叫做栈区,由编译器自动释放,存放函数的参数值,局部变量值等。
在C#中:Stack是指栈区,Heap是指托管堆。
堆栈的工作方式:
Int a=1; Double b=1.1;
图表 1未分配内存
图表 2已分配内存后
托管堆的工作方式:堆栈有非常高的性能,但要求变量生命周期必须嵌套(后进先出决定的),在很多情况下这种要求很过分,通常我们希望通过一个方法来分配内存,用来存储一些数据并在方法退出很久时间内数据任是可用的,用new运算符来请求控件,就实现了这种可能。
Void DoSomeThing() { Customer John;→声明一个Customer的应用John,包含Customer对象的地址,需4字节 Join=new Customer();→托管堆分配内存,假设对象占用了32个字节,.NET运行库关于在堆中搜索一块连续的未使用过的32个字节的控件,假定起始地址为200000 }
声明应用内存图解:

实例化对象内存图解:

★引用变量生存周期:只要堆栈中存在对它的引用,该变量就不会失效
2. 托管堆的垃圾回收机制
当对象不被引用时,会闪出堆中对应的数据,如果仅是这样,久而久之自由空间就会被分割开来,新对象需要分配内存时会很难处理,但托管堆垃圾回收器在运行时只要它释放能释放的对象,就将其他对象压缩,把他们推向堆的顶部,形成一个连续的块,虽看似造成性能损失,但给新对象分配内存提供了方便。
3. 装箱和拆箱
装箱过程:首先堆栈中分配一个字节的空间用来存储引用变量I。然后在托管堆中分配比之占空间大的区域来存储它的拷贝,多了一个方法表指针和一个SyncBlockIndex,再把地址赋给I。
拆箱过程:在堆栈分配的4个字节的控件来保存变量J,拷贝O的实例到J的内
代码:
{
int I=1;
object O=I;//装箱
int J=(int)O;拆箱
}
★拆箱注意:必须保证该值变量J有足够的空间来拆箱得到的值O。
4. 值类型与引用类型的复制过程
代码:
public class Point//若为结构体 struct { public int x; public int y; } Point p1=new Point(); p1.x=1; p1.y=2; Point p2=p1; p2.x=3; p2.y=4; Console.WriteLine p1.x,p1.y,p2.x,p2.y;
结果:3,4,3,4
结果:1,2,3,4
图解:共同指向托管堆的存值区域
★故:改变p2的值会影响p1,若Point为结构体(Struct),复制地址的同时连同值也一同复制,故不影响。


浙公网安备 33010602011771号