在 HotSpot 虚拟机中,对象在内存中存储的布局分为 3 块区域:算法
对象头记录了对象在运行过程当中所须要使用的一些数据:数组
对象头可能包含类型指针,经过该指针能肯定对象属于哪一个类。若是对象是一个数组,那么对象头还包括数组长度。函数
实例数据部分就是成员变量的值,其中包括父类成员变量和本类成员变量。布局
用于确保对象的总长度为 8 字节的整数倍。性能
HotSpot 要求对象的总长度为 8 字节的整数倍。因为对象头必定是 8 字节的整数倍,但实例数据部分的长度是任意的,所以须要对齐补充字段确保整个对象的总长度为 8 的整数倍。spa
对齐补充并非必然存在,也没有特别的含义,它仅仅起着占位符的做用。
虚拟机遇到一条 new 指令时,首先检查常量池中是否有这个类的符号引用,而且检查这个符号引用所表明的类是否已被加载、解析和初始化过。若是没有,那么必须先执行相应的类加载过程。线程
对象所需内存的大小在类加载完成后即可彻底肯定,接下来从堆中划分一块对应大小的内存空间给新的对象。分配堆中内存有两种方式:指针
若是 JVM 的垃圾收集器采用复制算法或标记-整理法,那么堆中空闲内存是完整的区域,而且空间内存和已使用内存之间由一个指针标记。为对象分配内存时,只需移动指针便可。这种在完整空闲区域上经过移动指针来分配内存的方式称为“指针碰撞”。对象
若是 JVM 的垃圾收集器采用标记-清除算法,那么堆中空闲区域和已使用区域交错,所以须要用一张“空闲列表”来记录堆中哪些区域是空闲区域,从而在建立对象的时候根据这张“空闲列表”找到空闲区域,并分配内存。内存
分配完内存后,为对象中的成员变量赋上初始值,设置对象头信息,调用对象的构造函数方法进行初始化。
至此,整个对象的建立过程就完成了。
全部对象的存储空间都是在堆中分配的,可是这个对象的引用倒是在堆栈中分配的。也就是说在创建一个对象时两个地方都分配内存,在堆中分配的内存实际创建这个对象,而在堆栈中分配的内存只是一个指向这个堆对象的指针(引用)而已。
那么根据引用存放的地址类型的不一样,对象有不一样的访问方式。
堆中须要有一块叫作“句柄池”的内存空间,句柄中包含了对象实例数据与类型数据各自的具体地址信息。
引用类型的变量存放的是该对象的句柄地址(reference)。访问对象时,首先须要经过引用类型的变量找到该对象的句柄,而后根据句柄中对象的地址找到对象。
引用类型的变量直接存放对象的地址,从而不须要句柄池,经过引用可以直接访问对象。但对象所在的内存空间须要额外的策略存储对象所属的类信息的地址。
HotSpot 采用直接指针方式访问对象,由于它只须要一次寻址操做,从而性能比句柄访问方式快一倍。但它须要额外的策略存储对象在方法区中类信息的地址。