类型有:基元类型(int、long、bool等编译器直接支持的类型)、值类型、引用类型。
所有的类型都派生自System.Object
当我们new一个对象时,new操作实际做了这些事情:
- 计算当前类型及其所有父类,且一直计算到
System.Object
中定义的所有实例字段需要的字节数,这些实例字段的“类型对象指针”和“同步块索引”对象所需要的字节数。 - 从堆中分配这些字节,并初始化为0
- 初始化类型对象指针和同步块索引
- 调用类的构造函数
类型对象指针:当我们想查看某个对象是属于什么类型时,一般会调用GetType()
或者typeof()
方法,这个方法返回这个对象的具体类型,那么这两个方法怎么知道这个对象是什么类型呢?就是根据这个对象里的这个类型对象指针来找的,类型对象本质也是对象,所以就返回了这个对象。
同步块索引:线程同步用的,比如当我们lock住了某个对象,另外一个线程也执行到这里时,操作系统怎么知道是该阻塞还是该放行呢?就是根据这个同步块索引。具体的实现可以参考线程同步那篇文章。
装箱和拆箱
概念都不用解释了。值类型比引用类型“快”,主要是因为它们不在堆中分配,不需要被GC,也不用通过指针进行引用。
装箱
值类型转为引用类型,对应参考代码(object)i
,主要发生了下面这些事情:
- 在托管堆中分配内存,所需内存的大小就是值类型各字段所需要的内存量+类型对象指针的内存量+同步块索引的内存量
- 将值类型的字段复制到新分配的堆内存中
- 返回这个对象的指针
拆箱
引用类型转为值类型,对应代码(int)obj
,主要发生了这件事:
- 获取已装箱对象中各个字段在堆上的地址
拆箱不是简单的把装箱倒过来操作,拆箱简单的多,就是一个获取地址的操作。对于以下代码:
int a=(int)obj;
其实有两步操作,第一步就是获取在堆上的地址,第二步是赋值操作引起的将堆上的内容复制到栈上的操作,第二步其实不属于拆箱。