基础知识1: 堆栈(书名:c#从现象到本质)
堆栈(heap stack)
- 堆可以分为托管堆和非托管堆,CLR管理托管堆和栈的垃圾回收。非托管堆由我们自己管理回收。
- 堆基于进程,属于进程空间的一部分。托管堆主要分为以下几个部分:
- GC堆(GC heap)
- 三个程序域
- 栈基于线程
三个程序域
系统域(开发者无法操作)
共享域(开发者无法操作)
应用程序域(AppDomain的一个实例) 每个AppDomain有自己的加载堆
加载堆(loader heap)
存在于每个程序域中,存放CLR的类型系统和用户自定义的类型对象,不同域的加载堆存放的对象不同。 AppDomain加载堆也存放静态对象,由于静态对象是全局的,不会成为垃圾,所以不受GC管辖。加载堆分为高频堆(32k),低频堆(8k)。比如值类型(或者引用类型)的静态变量就分配在加载堆上,所以值类型不一定分配在栈或托管堆上
GC堆(GC heap) 简单介绍
垃圾收集器的处理对象。分为0,1,2代,代越高越大。2代中有个特殊部分 大对象堆(large object heap)
它存放超过85k大小的对象。 // 小对象堆(SOH)
方法表指针(method table pointer) / 类型对象指针(TypeHandle)和同步块索引(synchronization block index)
同步块索引是类的标配,它位于类在堆上定义的开头-4(或-8 注释:32位 64位内存页大小) 至 0字节。 CLR管理一个同步块数组,共32、64多功能结构其中前6位提示访问者自身为0 , 1代表不同的功能, 比如线程同步,当对象被线程访问后会判断索引值是否为特殊值,如果否则不能操作这个实例,如果是则会在同步数组中分配一个同步块,并将此索引值写入实例的索引值。
方法表指针又称类型对象指针,类型对象由CLR在加载堆中创建,类型对象中存放了静态字段和方法表,所以反射获取到的类型就是它类型对象,并且指向同一个
内存对齐
消除排列顺序的不同带来的额外的内存页消耗
32位
bit
int
bit
这3个CLR会把它换成int bit bit
值类型引用类型
值类型一定是密封的
值类型不一定分配在栈上
- 值类型含有引用类型,会分配到托管堆上
- 值类型是静态变量分配到加载堆
- 局部变量升级为密封类(闭包)
装箱拆箱
装箱把值类型转为object或者实现的接口
- 在堆中申请内存
- 再加上类型对象和同步块索引
- 将值类型字段拷贝到新分配的内存上
- 返回对象的内存地址