HotSpot 虚拟机对象探秘
对象的内存布局
在 HotSpot 虚拟机中,对象的内存布局分为以下 3 块区域:
- 对象头(Header)
- 实例数据(Instance Data)
- 对齐填充(Padding)
对象头
对象头记录了对象在运行过程中所须要应用的一些数据:
- 哈希码
- GC 分代年龄
- 锁状态标记
- 线程持有的锁
- 偏差线程 ID
- 偏差工夫戳
对象头可能蕴含类型指针,通过该指针能确定对象属于哪个类。如果对象是一个数组,那么对象头还会包含数组长度。
实例数据
实例数据局部就是成员变量的值,其中包含父类成员变量和本类成员变量。
对齐填充
用于确保对象的总长度为 8 字节的整数倍。
HotSpot VM 的主动内存管理系统要求对象的大小必须是 8 字节的整数倍。而对象头局部正好是 8 字节的倍数(1 倍或 2 倍),因而,当对象实例数据局部没有对齐时,就须要通过对齐填充来补全。
对齐填充并不是必然存在,也没有特地的含意,它仅仅起着占位符的作用。
对象的创立过程
类加载查看
虚拟机在解析 .class
文件时,若遇到一条 new 指令,首先它会去查看常量池中是否有这个类的符号援用,并且查看这个符号援用所代表的类是否已被加载、解析和初始化过。如果没有,那么必须先执行相应的类加载过程。
为新生对象分配内存
对象所需内存的大小在类加载实现后便可齐全确定,接下来从堆中划分一块对应大小的内存空间给新的对象。调配堆中内存有两种形式:
- 指针碰撞
如果 Java 堆中内存相对规整 (阐明采纳的是“ 复制算法 ”或“ 标记整顿法 ”),闲暇内存和已应用内存两头放着一个指针作为分界点指示器,那么分配内存时只须要把指针向闲暇内存移动一段与对象大小一样的间隔,这种调配形式称为“ 指针碰撞”。
- 闲暇列表
如果 Java 堆中内存并不规整 ,已应用的内存和闲暇内存交织(阐明采纳的是 标记 - 革除法 ,有碎片),此时没法简略进行指针碰撞,VM 必须保护一个列表,记录其中哪些内存块闲暇可用。调配之时从闲暇列表中找到一块足够大的内存空间划分给对象实例。这种形式称为“ 闲暇列表”。
初始化
调配完内存后,为对象中的成员变量赋上初始值,设置对象头信息,调用对象的构造函数办法进行初始化。
至此,整个对象的创立过程就实现了。
对象的拜访形式
所有对象的存储空间都是在堆中调配的,然而这个对象的援用却是在堆栈中调配的。也就是说在建设一个对象时两个中央都分配内存,在堆中调配的内存理论建设这个对象,而在堆栈中调配的内存只是一个指向这个堆对象的指针(援用)而已。那么依据援用寄存的地址类型的不同,对象有不同的拜访形式。
句柄拜访形式
堆中须要有一块叫做“句柄池”的内存空间,句柄中蕴含了对象实例数据与类型数据各自的具体地址信息。
援用类型的变量寄存的是该对象的句柄地址(reference)。拜访对象时,首先须要通过援用类型的变量找到该对象的句柄,而后依据句柄中对象的地址找到对象。
间接指针拜访形式
援用类型的变量间接寄存对象的地址,从而不须要句柄池,通过援用可能间接拜访对象。但对象所在的内存空间须要额定的策略存储对象所属的类信息的地址。
须要阐明的是,HotSpot 采纳第二种形式,即间接指针形式来拜访对象,只须要一次寻址操作,所以在性能上比句柄拜访形式快一倍。但像下面所说,它须要 额定的策略 来存储对象在办法区中类信息的地址。
本文由 mdnice 多平台公布