- 对象内存布局
- 对象头
- 运行时元数据
- 类型指针(类元数据)
- 实例数据
- 对齐填充
- 对象内存布局之JOL 证明
- GC分代年龄说明
- 压缩指针参数
对象内存布局
兄弟们感兴趣的话,在 JVM 篇有对 对象的详细介绍:对象实例化内存布局
对象头
运行时元数据
运行时元数据(对象标记)Mark World,包括:
- 哈希值(HashCode)
- GC分代年龄
- 锁状态标志
- 线程持有的锁
- 偏向线程ID
- 翩向时间戳
如果是数组的实例部分还包括数组的长度,
在64位系统中,Mark Word占了8个字节,类型指针占了8个字节,一共是16个字节(不考虑压缩指针)
mark word(64位)分布图,对象布局、GC回收和后面的锁升级就是对象标记MarkWord里面标志位的变化
类型指针(类元数据)
类型指针(类元数据)Klass Pointer
对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例。
实例数据
存放类的属性(Field)数据信息,包括父类的属性信息,这部分内存按4字节对齐。
对齐填充
虚拟机要求对象起始地址必须是8字节的整数倍。
填充数据不是必须存在的,仅仅是为了字节对齐,这部分内存按8字节补充对齐。
class Customer{
int i ;
boolean flag;
}
Customer对象头占用16个字节,i 占有 4 个字节,boolean占用 1 个字节,总共 21 个字节,但是 jvm 要求对象占用的字节必须为 8 的倍数,因此填充为 24 个字节。
对象内存布局之JOL 证明
显示的表示对象内存布局
JOL官网
增加依赖:
<!--
官网:http://openjdk.java.net/projects/code-tools/jol/
定位:分析对象在JVM的大小和分布
-->
<dependency>
<groupId>org.openjdk.jol</groupId>
<artifactId>jol-core</artifactId>
<version>0.9</version>
</dependency>
代码:
public class JOLTest {
public static void main(String[] args) {
// //VM的细节详细情况
// System.out.println(VM.current().details());
// //所有的对象分配的字节都是8的整数倍。
// System.out.println(VM.current().objectAlignment());
Object o = new Object();
System.out.println( ClassLayout.parseInstance(o).toPrintable());
}
}
输出结果:
- OFFSET 偏移量,也就是到这个字段位置所占用的byte数
- SIZE 后面类型的字节大小
- TYPE 是Class中定义的类型
- DESCRIPTION DESCRIPTION是类型的描述
- VALUE VALUE是TYPE在内存中的值
类型指针占用 4 字节,是因为默认开启了 压缩指针。
查看自定义类的内存布局:
public class JOLTest {
public static void main(String[] args) {
Customer customer = new Customer();
System.out.println( ClassLayout.parseInstance(customer).toPrintable());
}
}
class Customer{
int i ;
boolean flag ;
}
输出结果:
GC分代年龄说明
GC年龄采用4位bit存储,最大为15,
例如MaxTenuringThreshold参数默认值就是15
如果设置 阈值大于 15,就会报错
压缩指针参数
jvm 中默认开启了 压缩指针。主要是为了节省内存空间
默认开启压缩指针,对象内存情况:
对象标记 8 个字节,类型指针 4 个字节,对齐填充 4 个字节 = 16 个字节
关闭压缩指针,对象内存情况:-XX:-UseCompressedClassPointers
对象标记 8 个字节,类型指针 8 个字节就对应上了。
各位彭于晏,如有收获点个赞不过分吧…✌✌✌
gongzhonghao 回复 [JUC] 获取MarkDown笔记