Java对象的内存布局
在介绍Java对象的内存布局之前先介绍一下Java中哪些数据是以对象的形式创建的
- Java的数据类型
- 基本数据类型
- byte、short、int、long、float、double、char、boolean
- 基本数据类型被创建时,在栈上给其划分一块内存,将数值直接存储在栈上
- 引用数据类型
- 类、接口、数组、枚举、注解等
- 引用数据类型在创建时首先要在栈上给其引用分配一块内存,将对象的实例存储在堆内存上,栈上面的引用指向堆内存中对象的地址
- 基本数据类型
Java中在内存布局时主要考虑了两种内存布局,一种是普通对象,就是类、接口、枚举、注解等的实例,一种是数组对象,即数组的实例
Java普通对象的内存布局
-
Java普通对象的内存布局图如下
-
markword
用来标记锁信息、GC信息、IdentityHashCode信息等,32位JVM是4字节,64位JVM中是8字节
在openjdk的源码包中(/openjdk/hotspot/src/share/vm/oops)可以找到对应的C++markOop.hpp文件,从注释中可以看到markword分别在32位和64位JVM中的组成
具体组成见下面两张图
32位JVM中markword的组成
64位JVM中markword的组成
虽然它们在不同位数的JVM中长度不一样,但是基本组成内容是一致的。- 锁标志位(lock):区分锁状态,11时表示对象待GC回收状态, 只有最后2位锁标识(11)有效。
- biased_lock:是否偏向锁,由于正常锁和偏向锁的锁标识都是 01,没办法区分,这里引入一位的偏向锁标识位。
- 分代年龄(age):表示对象被GC的次数,当该次数到达阈值的时候,对象就会转移到老年代。
- 对象的hashcode(hash):运行期间调用System.identityHashCode()来计算,延迟计算,并把结果赋值到这里。当对象加锁后,计算的结果31位不够表示,在偏向锁,轻量锁,重量锁,hashcode会被转移到Monitor中。
- 偏向锁的线程ID(JavaThread):偏向模式的时候,当某个线程持有对象的时候,对象这里就会被置为该线程的ID。 在后面的操作中,就无需再进行尝试获取锁的动作。
- epoch:偏向锁在CAS锁操作过程中,偏向性标识,表示对象更偏向哪个锁。
- ptr_to_lock_record:轻量级锁状态下,指向栈中锁记录的指针。当锁获取是无竞争的时,JVM使用原子操作而不是OS互斥。这种技术称为轻量级锁定。在轻量级锁定的情况下,JVM通过CAS操作在对象的标题字中设置指向锁记录的指针。
- ptr_to_heavyweight_monitor:重量级锁状态下,指向对象监视器Monitor的指针。如果两个不同的线程同时在同一个对象上竞争,则必须将轻量级锁定升级到Monitor以管理等待的线程。在重量级锁定的情况下,JVM在对象的ptr_to_heavyweight_monitor设置指向Monitor的指针。
-
klass pointer
类指针,用于标记该对象是哪一个Class类对象(Object.class)的实例
JVM可以选择开启和关闭指针压缩,JVM是64位操作系统的话,开启后4字节,不开启8字节,默认开启。
设置指针内存压缩的指令为
-XX:+UseCompressedOops
取消指针内存压缩的指令为
-XX:-UseCompressedOops
-
instance data
大小由成员变量的类型和数量决定的,如果没有成员变量则这里为空,存放基本变量或变量引用
-
padding
一个对象占用的字节数默认必须是8的倍数,不足用padding补齐。
若没有引用(new Object()),则直接判断是否被8整除,不足的用padding补齐
若存在引用(Object obj = new Object()),则将引用对应的4字节加上,然后判断是否被8整除,不足用padding补齐
JVM可以设置更改默认值8,使用指令
-XX:ObjectAlignmentInBytes
Java数组对象的内存布局
-
Java数组对象的内存布局图如下
-
数据对象的内存布局和普通对象内存布局基本类似,数组对象的内存布局Header中多了一个数组长度
length
-
length
标记数组有多少个元素,4字节
-
instance data
根据数组类型m和数组n长度决定的,占m*n字节
如果数组元素为基本数据类型,byte/boolean/…,m为对应的长度
工具包JOL(Java Object Layout)
-
可以查看JVM中对象的内存布局
-
在maven中添加引用
<dependency> <groupId>org.openjdk.jol</groupId> <artifactId>jol-core</artifactId> <version>0.7.1</version> </dependency>
-
案例1,测试没有成员变量情况
import org.openjdk.jol.info.ClassLayout; public class Test{ public static void main(String[] args){ Object obj = new Object(); System.out.println(ClassLayout.parseInstance(obj).toPrintable()); } }
输出结果为
因为没有成员变量,因此前两个header是markword,第三个header是压缩后的Klass pointer,因为加起来是12字节,所以需要paading补齐4字节
-
案例2,测试含有成员变量的情况
import org.openjdk.jol.info.ClassLayout; public class Test{ public static void main(String[] args){ Obj obj = new Obj(); System.out.println(ClassLayout.parseInstance(obj).toPrintable()); } }
输出结果为
因为含有成员变量,因此前两个header是markword,第三个header是压缩后的Klass pointer,第四行是成员变量的大小4字节,因为加起来刚好是16字节,所以不需要paading补齐
-
案例3,测试有锁的情况
import org.openjdk.jol.info.ClassLayout; public class Test{ public static void main(String[] args){ Object obj = new Object(); synchronized(obj){ System.out.println(ClassLayout.parseInstance(obj).toPrintable()); } } }
输出结果为
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OrOUEYpz-1603781321223)(https://raw.githubusercontent.com/EsonW/picgo/master/image-20201027092601905.png)]
因为没有成员变量,因此前两个header是markword,第三个header是压缩后的Klass pointer,因为加起来是12字节,所以需要paading补齐4字节
第一行的value可以看到,和没有锁的是不一样的,说明markword里保存了锁的相关信息
-
案例4,测试数组的情况
import org.openjdk.jol.info.ClassLayout; public class Test{ public static void main(String[] args){ int[] arr = {1, 2, 3, 4}; System.out.println(ClassLayout.parseInstance(arr).toPrintable()); } }
输出结果为
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2tvJh59i-1603781321226)(C:%5CUsers%5CAdministrator%5CAppData%5CRoaming%5CTypora%5Ctypora-user-images%5Cimage-20201027093113564.png)]
因为没有成员变量,因此前两个header是markword,第三个header是压缩后的Klass pointer,第四个header是数组长度length,数组元素占16字节,因为加起来是32字节,所以不需要paading补齐
-
案例5,测试取消指针压缩,值JVM指令
-XX:-UseCompressedOops
import org.openjdk.jol.info.ClassLayout; public class Test{ public static void main(String[] args){ Object obj = new Object(); System.out.println(ClassLayout.parseInstance(obj).toPrintable()); } }
因为没有成员变量,因此前两个header是markword,第三四个header是未压缩的Klass pointer,因为加起来是16字节,所以不需要paading补齐
参考链接
https://www.cnblogs.com/JonaLin/p/13864578.html
https://www.cnblogs.com/zxj-study/p/13057490.html