对象内存布局
对象头
对象头包含了两部分,分别是 运行时元数据(Mark Word)
和 类型指针
如果是数组,还需要记录数组的长度
运行时元数据(Mark Word)
- 哈希值(HashCode)
- GC分代年龄
- 锁状态标志
- 线程持有的锁
- 偏向线程ID
- 翩向时间戳
类型指针
指向类元数据InstanceKlass
,确定该对象所属的类型。指向的其实是方法区中存放的类元信息
实例数据(Instance Data)
实例数据
部分是对象真正存储的有效信息,也既是我们在程序代码里面所定义的各种类型的字段内容,无论是从父类继承
下来的,还是在子类中定义的都需要记录下来。这部分的存储顺序会受到虚拟机分配策略参数
FieldsAllocationStyle
和字段在Java源码中定义顺序的影响。HotSpot虚拟机 默认的分配策略为longs/doubles
、ints
、shorts/chars
、bytes/booleans
、oops(Ordinary
Object Pointers),从分配策略中可以看出,相同宽度的字段总是被分配到一起。在满足这个前提条件的情况下,在父类中定义的变量会出现在子类之前。如果CompactFields
参数值为true(默认为true),那子类之中较窄的变量也可能会插入到父类变量的空隙之中。
对其填充(Padding)
对齐填充并不是必然存在的,也没有特别的含义,它仅仅起着占位符的作用。由于HotSpotVM的自动内存管理系统要求对象起始地址必须是8字节的整数倍,换句话说就是对象的大小必须是
8字节
的整数倍·。对象头正好是8字节的倍数(1倍或者2倍),因此当对象实例数据部分没有对齐
的话,就需要通过对齐填充
来补全。
总结
Object obj= new Object() 做了什么操作,申请了哪些内存占用的大小?
- new Object (); 创建一个User对象,内存分配在堆上
- Object obj; 创建一个引用,内存分配在栈上
- = 将Object 对象地址赋值给引用
指针压缩参数设置:
-XX:+UseCompressedOops 开启指针压缩
-XX:-UseCompressedOops 关闭指针压缩
-
未开启指针压缩 占用大小为:8(Mark Word)+8(Class Pointer)=16字节
-
开启了指针压缩(默认是开启的) 开启指针压缩后,Class Pointer会被压缩为4字节,最终大小为: 8(Mark Word)+4(Class Pointer)+4(对齐填充)=16字节
引入坐标
<dependency>
<groupId>org.openjdk.jol</groupId>
<artifactId>jol-core</artifactId>
<version>0.10</version>
</dependency>
Object的Demo(开启指针压缩)
public static void main(String[] args){
Object o = new Object();
System.out.println(ClassLayout.parseInstance(o).toPrintable());
}
Object的Demo(关闭指针压缩)