Java 对象内存布局
Posted 嘿咻、晚安喵
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java 对象内存布局相关的知识,希望对你有一定的参考价值。
1、对象头(Header)
- 对象自身运行时数据(mark word):hash 码、GC 分代年龄、锁状态标识、是否偏向锁、线程持有的锁、偏向线程ID、偏向时间戳;
- 类型指针:对象指向元数据的指针。
mark word:
对象头信息与对象自身定义的数据无关的额外存储成本,考虑到虚拟机的空间效率,对象头信息被设计成一个非固定的数据结构以便在极小的空间内存储尽量多的信息 ,它对根据对象的状态复用自己的存储空间。
下图描述 32位虚拟机 上,对象不同状态时 mark word 比特位区间的含义:
锁状态 | 25bit | 4bit | 1bit | 2bit | |
23bit | 2bit | 是否偏向 | 锁标志位 | ||
无锁态 | 对象的hashCode | 分代年龄 | 0 | 01 | |
轻量级锁 | 指向栈中锁记录的指针 | 00 | |||
重量级锁 | 指向互斥量(重量级锁)的指针 | 10 | |||
GC标记 | 空 | 11 | |||
偏向锁 | 线程ID | Epoch | 分代年龄 | 1 | 01 |
查找对象元数据的两种方式:
- 句柄方式:堆中维护一个句柄池,句柄包含类型数据指针和对象数据指针(栈中的 reference 指向句柄池中的句柄,句柄指向堆中的实例,句柄指向方法区的类型数据);
- 直接指针:堆中的对象直接包含类型数据指针(栈中的 reference 指向堆中对象,对象指向方法区中的类型数据 )。
查找元数据两种方式的区别:
- 句柄方式:栈中的 reference 稳定,需要另外开辟一块区域(堆中的句柄池),对象移动时,需要修改句柄中实例数据的地址;
- 直接指针:节省一次指针定位开销,速度更快。
2、实例数据(Instance Data)
各种类型的字段内容(父类 + 子类)。
3、对齐填充(Padding)
占位符(没有其他作用),虚拟机内存管理系统要求对象起始位置必须是 8 的整数倍,实例数据部分没有对齐,则填充。
补充:
文中的插图,可以衍生出很多面试题,如:
1、GC 分代年龄为什么默认为 15?
2、对象锁状态类型有几种?
3、为什么要多 1bit 来标记是否偏向?
......
以上是关于Java 对象内存布局的主要内容,如果未能解决你的问题,请参考以下文章