0. 概述
一个对象,除了我们所看到的实例数据外,还有两个隐藏的部分,一部分叫对象头,另外一部分叫对齐填充。
1. 实例数据与对齐填充
1.1 实例数据
这两个相对好理解一些所以放在前面一起讲了,实例数据就是我们定义的变量或者方法一类的数据。
1.2 对齐填充
对齐填充出现的原因是因为JVM需要将对象放入内存,为防止内存碎片,要求了Java中所有对象大小都必须为8bit的倍数,但实际定义中总会用不满足这个条件的对象,对齐填充的作用就是将这些不满足的对象填充到一个满足这个条件的水平。
2. 对象头
2.0 对象头概述
对象头中存放了三类信息,Class Pointer,Array Length(数组独有),Mark Word。我同样以简单的到繁琐的顺序叙述。
2.1 Class Pointer
存储内容是类信息,JVM用来确定此对象的类型。指针长度为一个字大小,也就是说在64位机器中占64位,32位机器中占32位。可通过+UseCompressedOops
开启指针压缩,其中,oop的意思为普通对象指针(ordinary object pointer)。开启该选项后,下列指针将压缩至32位:
- 每个Class的属性指针(即静态变量)
- 每个对象的属性指针(即对象变量)
- 普通对象数组的每个元素指针
当然,也不是所有的指针都会压缩,一些特殊类型的指针JVM不会优化,比如指向PermGen的Class对象指针(JDK8中指向元空间的Class对象指针)、本地变量、堆栈元素、入参、返回值和NULL指针等。
2.2 Array Length
其实名称已经说明了它的作用,JVM用这个值来确定数组长度,这个值只有数组的对象头中会出现,普通对象没有。此对象不管32位还是64位电脑,均占32位。
2.3 Mark Word
这部分主要用来存储对象自身的运行时数据,如HashCode、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等等。Mark Word的位长度为JVM的一个字,也就是说32位JVM的Mark Word为32位,64位JVM为64位。
|-------------------------------------------------------|--------------------|
| Mark Word (32 bits) | State |
|-------------------------------------------------------|--------------------|
| identity_hashcode:25 | age:4 | biased_lock:0 | 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 |
|-------------------------------------------------------|--------------------|
|------------------------------------------------------------------------------|--------------------|
| Mark Word (64 bits) | State |
|------------------------------------------------------------------------------|--------------------|
| unused:25 | identity_hashcode:31 | unused:1 | age:4 | biased_lock:0 | 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:对象是否启用偏向锁标记,只占1个二进制位。为1时表示对象启用偏向锁,为0时表示对象没有偏向锁。
biased_lock | lock | 状态 |
---|---|---|
0 | 01 | 无锁 |
1 | 01 | 偏向锁 |
0 | 00 | 轻量级锁 |
0 | 10 | 重量级锁 |
0 | 11 | GC标记 |
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的指针。