1对象内存结构说明
注意:
如果对象为数组对象,在对象头后面有4字节存储数组长度;
1.1对象头
对象头分为Mark Word和Class Pointer两部分;
Mark Word:对象基础信息
- 32位操作系统中占4字节,64位操作系统中占8字节;
- 对于不同状态(无锁态,轻量级锁,重量级锁,GC标记,偏向锁)的对象,Mark Word中包含的信息不相同;
- 主要包含哈希码(HashCode)、GC分代年龄、锁状态标志等信息。
Klass Pointer:类元数据指针
- 32位操作系统中占4字节
- 64位操作系统中占8字节,开启指针压缩时占4字节
- 表明对象属于哪个类型
- 类元数据存在方法区,记录这个类有哪些属性,哪些方法,以及方法的代码,
- 与Class对象不同,Class对像为类加载完后生成的java对象,存放在堆空间,提供反射机制使用,其底层也是借用类元信息完成反射功能。
1.2实例数据
对象属性的值数据
1.3填充字节
默认8字节对齐;
保证对象的大小是8的整数倍;
2 通过jol计算对象大小
依赖
<dependency>
<groupId>org.openjdk.jol</groupId>
<artifactId>jol-core</artifactId>
<version>0.10</version>
</dependency>
分析Object对象大小
- 代码
public static void main(String[] args) {
ClassLayout classLayout = ClassLayout.parseInstance(new Object());
System.out.println("对象信息===================");
System.out.println(classLayout.toPrintable());
System.out.println("类信息===================");
classLayout = ClassLayout.parseClass(Object.class);
System.out.println(classLayout.toPrintable());
}
- 输出
分析int[]对象大小
- 代码
public static void main(String[] args) {
ClassLayout classLayout = ClassLayout.parseInstance(new int[]{1,2,3});
System.out.println("对象信息===================");
System.out.println(classLayout.toPrintable());
System.out.println("类信息===================");
classLayout = ClassLayout.parseClass(int[].class);
System.out.println(classLayout.toPrintable());
}
- 输出
分析自定义类App对象大小
- 代码
public class App {
private Integer a;
private int b;
private Byte d;
private byte e;
public static void main(String[] args) {
ClassLayout classLayout = ClassLayout.parseInstance(new App());
System.out.println("对象信息===================");
System.out.println(classLayout.toPrintable());
System.out.println("类信息===================");
classLayout = ClassLayout.parseClass(App.class);
System.out.println(classLayout.toPrintable());
}
}
- 输出
结论
- jvm中数组长度存储为4字节,则数组最大长度为Integer的最大值
- jvm对象中只有属性信息,方法信息通过class pointer找到类元数据获取
- jvm对象中属性包含byte,char,short等不足4字节的基础类型数据时,会按照基础类型先排列的原则,如果基础类型所有属性所占字节数非4的整数倍,则需要填充内部对齐字节满足4的整数倍
- jvm对象中属性按照先基础类型后包装类型顺序排列
- jvm对象中属性为基础类型或包装类型,所占存储不一样
- jvm对象中属性为包装类型时,存储的是包装类型对象的内存地址,需要二次寻址确定其对应的值
思考:
既然包装类型和基础类型所占空间大小不一样,并且包装类型数据需要二次寻址,那么在日常编程中什么情况下需要用包装类型?什么情况下需要用基础类型?
3 指针压缩
- 压缩的是jvm中对象的内存地址值
- 使用jvm参数-XX:+UseCompressedOops开启指针压缩
- jdk8默认开启
- jol分析指针压缩