java 在堆中的内存分为三个部分
- 对象头
Object header
- 值
value
- 对齐
padding
(不一定存在,java 内存需要对齐 8byte,不足部分填充)
java 对象头
1 | |--------------------------------------------------------------| |
java 数组头
1 | |---------------------------------------------------------------------------------| |
其中Mark Word
1 | |-------------------------------------------------------|--------------------| |
1 | |------------------------------------------------------------------------------|--------------------| |
lock:2 位的锁状态标记位,由于希望用尽可能少的二进制位表示尽可能多的信息,所以设置了 lock 标记。该标记的值不同,整个 mark word 表示的含义不同。
biased_lock | Tlock | 状态 |
---|---|---|
0 | 01 | 无锁 |
1 | 01 | 偏向锁 |
0 | 00 | 轻量级锁 |
0 | 10 | 重量级锁 |
0 | 11 | GC 标记 |
biased_lock`:对象是否启用偏向锁标记,只占 1 个二进制位。为 1 时表示对象启用偏向锁,为 0 时表示对象没有偏向锁。
age
:4 位的Java
对象年龄。在GC
中,如果对象在Survivor
区复制一次,年龄增加 1。当对象达到设定的阈值时,将会晋升到老年代。默认情况下,并行GC
的年龄阈值为 15,并发GC
的年龄阈值为 6。由于age
只有 4 位,所以最大值为 15,这就是-XX:MaxTenuringThreshold
选项最大值为 15 的原因。identity_hashcode
:25 位的对象标识Hash
码,采用延迟加载技术。调用方法System.identityHashCode()
计算,并会将结果写到该对象头中。当对象被锁定时,该值会移动到管程Monitor
中。thread
:持有偏向锁的线程ID
。epoch
:偏向时间戳。ptr_to_lock_record
:指向栈中锁记录的指针。ptr_to_heavyweight_monitor
:指向管程Monitor
的指针。
class pointer
这一部分用于存储对象的类型指针,该指针指向它的类元数据,JVM
通过这个指针确定对象是哪个类的实例。该指针的位长度为 JVM
的一个字大小,即 32 位的 JVM
为 32 位,64 位的 JVM
为 64 位。
array length
如果对象是一个数组,那么对象头还需要有额外的空间用于存储数组的长度,这部分数据的长度也随着 JVM
架构的不同而不同:32 位的 JVM
上,长度为 32 位;64 位 JVM
则为 64 位。64 位 JVM
如果开启+UseCompressedOops
选项,该区域长度也将由 64 位压缩至 32 位。
数组下标寻址
首先根据栈上的指针找到该数组对象在内存中的位置
p
判断
index
是否越界,即与数组对象头中存储的array length
做比较根据数组对象头的
class pointer
确定数组元素的内存占用长度n
根据数组对象头长度
base
和下标计算出访问的元素的内存位置,即p+base+n*index