越发内卷的行业,使得程序员不得不去了解更多底层设计的东西,所以知识必须适可而止地总结一丢丢。
Java 对象由三部分区域组成,分别是
实例数据是,对象真正存储数据区域,包含了各种类型的字段。
例如 boolean 类型的字段占 1 字节,int 类型字段占 4 字节,long 类型字段占 8 字节。
HotSpot JVM 对内存的管理规范中,对象的大小必须是 8 字节的整倍数,所以当对象头 + 实例数据正好是 8 字节整倍数时,对齐填充就为 0。
如果不等于 8 字节整倍数,则需要补充字节占位,直到成为 8 字节整倍数。
Java 对象中对象头是最为复杂的一个区域,在 32 位 JVM 中占 8 字节,在 64 位 JVM 中占 16 字节,如果开启了压缩,则是占 12 字节。
对象头又包含两部分,mark word 、klass pointer。
http://openjdk.java.net/groups/hotspot/docs/HotSpotGlossary.html
mark word
The first word of every object header.
Usually a set of bitfields including synchronization state
and identity hash code. May also be a pointer
(with characteristic low bit encoding) to synchronization
related information. During GC, may contain GC state bits.
klass pointer
The second word of every object header.
Points to another object (a metaobject) which describes
the layout and behavior of the original object.
For Java objects, the "klass" contains a C++ style "vtable".
mark word:存放对象运行时的一些状态信息,gc 年龄、锁状态、hashCode 等。
klass pointer:指向对象类元数据的指针,虚拟机可以通过这个指针来确定对象是什么类型。
额外的,如果是数组类型,还会有 4 字节的 int 来标识数组的长度。
https://gist.github.com/arturmkrtchyan/43d6135e8a15798cc46c
64 位 JVM 中,Mark Word 占 8 字节。
是否是偏向锁
锁状态
open jdk 源码中对 mark word 的定义 https://github.com/unofficial-openjdk/openjdk/blob/jdk8u/jdk8u/hotspot/src/share/vm/oops/markOop.hpp
enum { locked_value = 0, // 0 00 轻量级锁
unlocked_value = 1, // 0 01 无锁
monitor_value = 2, // 0 10 重量级锁
marked_value = 3, // 0 11 gc 标志
biased_lock_pattern = 5 // 1 01 偏向锁
};
在对象处于不同状态时,Mark Word 前 61 位也会动态变化。
例如当 Synchronized
锁升级过程中,偏向锁
是 54 bit 来保存持有锁线程的地址,2 bit 来保存 epoch 值。
轻量级锁
则用 62 bit 来保存持有锁线程栈帧中 lockRecord
区的地址。而 重量级锁
则用 62 bit 保存了指向 monitor 对象的地址。
Mark Word 中使用 4 bit 来代表 对象的年龄,由此可见年龄的范围在 0-15,所以年龄最大只能到 15,还存活的对象将从 S 区被移至老年代。
引入 org.openjdk.jol 包,可以打印出来对象的组成。
<dependency>
<groupId>org.openjdk.jol</groupId>
<artifactId>jol-core</artifactId>
<version>0.14</version>
</dependency>
新建一个测试类,来打印对象的组成。
package com.lyqiang.sync.test;
import org.openjdk.jol.info.ClassLayout;
public class Test {
public static void main(String[] args) {
ObjA objA = new ObjA();
System.out.println(ClassLayout.parseInstance(objA).toPrintable());
}
}
class ObjA {
boolean b;
int i;
}
输出结果如下:
可以看出,对象头 object header
占了 12 字节,实例数据中 int 类型占 4 字节,boolean 类型占 1 字节,共 5 字节。对象头加实例数据共 17 字节,所以补了 7 字节,凑成 8 的整数倍 24 字节。
了解对象的组成,是认识对象大小的计算和 Synchronized
原理的必学一课。