JVM系列 - 对象探索
Publish date: Oct 279, 27029
Last updated: Oct 289, 28089
Last updated: Oct 289, 28089
问
在 HotSpot 虚拟机中:
- 如何在堆中分配 Java 对象
- Java 对象的布局
- 如何访问堆中的 Java 对象
对象分配
步骤
- JVM 遇到一条字节码 new 指令,检查该指令 参数 能否在 常量池 中定位到一个 类的符号引用,并检查该符号引用代表的类 是否已被加载、解析和初始化过,若没有,则先进行类加载,否则分配内存
- 分配内存时,可采取 指针碰撞 和 空闲列表 两种方式,取决于当前 Java 堆是否规整。Java 堆是否规整取决于垃圾收集器是否带空间压缩整理的功能。Serial 和 ParNew 带压缩整理,CMS 收集器基本上不带
- 分配内存如何在并发情况下保证线程安全:使用CAS加失败重试 和 TLAB策略。JVM 通过参数
-XX:+/-UseTLAB
设定是否使用 TLAB(Thread Local Allocation Buffer)
对象布局
在 HotSpot 虚拟机中,对象的内存布局可以划分为:
- 对象头(Header)
- 实例数据(Instance Data)
- 对齐填充(Padding)
对齐填充
不是必然存在的,仅仅起着 占位符 的作用
HotSpot 虚拟机要求 对象起始地址必须是 8 字节的整数倍
即 对象大小必须是 8 字节的整数倍
对象头已经是 8 字节的整数倍,所以:
如果实例数据部分不是 8 字节的整数倍,就通过对齐填充来补全
实例数据
对象真正存储的有效信息
在程序代码中定义的各种类型的字段内容
HotSpot 虚拟机默认的分配顺序是:
longs/doubles、ints、shorts/chars、bytes/booleans、oops
对象头
包含两类信息:
- 类型指针
- Mark Word
类型指针
对象指向它的类型元数据的指针,JVM 通过该指针确认该对象是哪个类的实例
Mark Word
存储对象自身的运行时数据,如:
- 哈希码(HashCode)
- GC 分代年龄
- 锁状态标志
- 线程持有的锁
- 偏向线程 ID
- 偏向时间戳
长度在 32 位和 64 位的虚拟机中分别为 32 个比特和 64 个比特
对象访问
对象创建好之后,Java 程序通过栈上的 reference 数据来操作堆上的具体对象
主流的访问方式:
句柄访问
优点:reference 中存储的是稳定句柄地址,在对象被移动时只会改变句柄中的实例数据指针,reference 本身不需要被修改
缺点:多一次指针定位的时间开销
直接指针访问
优点:速度更快,节省了一次指针定位的时间开销,HotSpot 虚拟机采用此方式
缺点:reference 不稳定,需要本身被修改