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