java数组

java 在堆中的内存分为三个部分

  1. 对象头 Object header
  2. value
  3. 对齐 padding(不一定存在,java 内存需要对齐 8byte,不足部分填充)

java 对象头

1
2
3
4
5
|--------------------------------------------------------------|
| Object Header (64 bits) |
|------------------------------------|-------------------------|
| Mark Word (32 bits) | Klass Word (32 bits) |
|------------------------------------|-------------------------|

java 数组头

1
2
3
4
5
|---------------------------------------------------------------------------------|
| Object Header (96 bits) |
|--------------------------------|-----------------------|------------------------|
| Mark Word(32bits) | Klass Word(32bits) | array length(32bits) |
|--------------------------------|-----------------------|------------------------|

其中Mark Word

1
2
3
4
5
6
7
8
9
10
11
12
13
|-------------------------------------------------------|--------------------|
| Mark Word (32 bits) | State |
|-------------------------------------------------------|--------------------|
| identity_hashcode:25 | age:4 | biased_lock:1 | lock:2 | Normal |
|-------------------------------------------------------|--------------------|
| thread:23 | epoch:2 | age:4 | biased_lock:1 | lock:2 | Biased |
|-------------------------------------------------------|--------------------|
| ptr_to_lock_record:30 | lock:2 | Lightweight Locked |
|-------------------------------------------------------|--------------------|
| ptr_to_heavyweight_monitor:30 | lock:2 | Heavyweight Locked |
|-------------------------------------------------------|--------------------|
| | lock:2 | Marked for GC |
|-------------------------------------------------------|--------------------|
1
2
3
4
5
6
7
8
9
10
11
12
13
|------------------------------------------------------------------------------|--------------------|
| Mark Word (64 bits) | State |
|------------------------------------------------------------------------------|--------------------|
| unused:25 | identity_hashcode:31 | unused:1 | age:4 | biased_lock:1 | lock:2 | Normal |
|------------------------------------------------------------------------------|--------------------|
| thread:54 | epoch:2 | unused:1 | age:4 | biased_lock:1 | lock:2 | Biased |
|------------------------------------------------------------------------------|--------------------|
| ptr_to_lock_record:62 | lock:2 | Lightweight Locked |
|------------------------------------------------------------------------------|--------------------|
| ptr_to_heavyweight_monitor:62 | lock:2 | Heavyweight Locked |
|------------------------------------------------------------------------------|--------------------|
| | lock:2 | Marked for GC |
|------------------------------------------------------------------------------|--------------------|

lock:2 位的锁状态标记位,由于希望用尽可能少的二进制位表示尽可能多的信息,所以设置了 lock 标记。该标记的值不同,整个 mark word 表示的含义不同。

biased_lock Tlock 状态
0 01 无锁
1 01 偏向锁
0 00 轻量级锁
0 10 重量级锁
0 11 GC 标记
  1. biased_lock`:对象是否启用偏向锁标记,只占 1 个二进制位。为 1 时表示对象启用偏向锁,为 0 时表示对象没有偏向锁。

  2. age:4 位的 Java 对象年龄。在 GC 中,如果对象在 Survivor 区复制一次,年龄增加 1。当对象达到设定的阈值时,将会晋升到老年代。默认情况下,并行 GC 的年龄阈值为 15,并发 GC 的年龄阈值为 6。由于 age 只有 4 位,所以最大值为 15,这就是-XX:MaxTenuringThreshold 选项最大值为 15 的原因。

  3. identity_hashcode:25 位的对象标识 Hash 码,采用延迟加载技术。调用方法 System.identityHashCode()计算,并会将结果写到该对象头中。当对象被锁定时,该值会移动到管程 Monitor 中。

  4. thread:持有偏向锁的线程 ID

  5. epoch:偏向时间戳。

  6. ptr_to_lock_record:指向栈中锁记录的指针。

  7. 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 位。

数组下标寻址

  1. 首先根据栈上的指针找到该数组对象在内存中的位置p

  2. 判断index是否越界,即与数组对象头中存储的array length做比较

  3. 根据数组对象头的class pointer确定数组元素的内存占用长度n

  4. 根据数组对象头长度base和下标计算出访问的元素的内存位置,即p+base+n*index