一、对象在堆内存中布局
1. 官方定义
在HotSpot虚拟机里,对象在堆内存中的存储布局可以划分为三个部分: 对象头(Header), 实例数据(Instance Data), 对齐填充(Padding).
2. 对象在堆内存中的存储布局
2.1 对象头
2.1.1 对象标记Mark Word
Mark Word保存什么?
默认存储对象的HashCode、分代年龄和锁标志位等信息。
这些信息都是与对象自身定义无关的数据,所以MarkWord被设计成一个非固定的数据结构以便在极小的空间内存存储尽量多的数据。
它会根据对象的状态复用自己的存储空间,也就是说在运行期间MarkWord里存储的数据会随着锁标志位的变化而变化。
在64位系统中,Mark Word占了8个字节,类型指针占了8个字节,一共是16个字节
2.1.2 类元信息(又叫类型指针)
类型指针指向方法区中类的元数据.
对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例。
2.1.3 对象头多大
在64位系统中,Mark Word占了8个字节,类型指针占了8个字节,一共是16个字节。
(在实际运行的过程中,JVM会默认开启压缩指针,压缩后对象头没有16个子节这么大.)
2.2 实例数据
存放类的属性(Field)数据信息,包括父类的属性信息,
如果是数组的实例部分还包括数组的长度,这部分内存按4字节对齐。
2.3 对齐填充
虚拟机要求对象起始地址必须是8字节的整数倍。填充数据不是必须存在的,仅仅是为了字节对齐, 这部分内存按8字节补充对齐。
3. 官网理论
Hotspot术语表官网
http://openjdk.java.net/groups/hotspot/docs/HotSpotGlossary.html
底层源码理论证明
http://hg.openjdk.java.net/jdk8u/jdk8u/hotspot/file/89fb452b3688/src/share/vm/oops/oop.hpp
_mark
字段是 mark word,_metadata
是类指针 klass pointer,
对象头(object header)即是由这两个字段组成,这些术语可以参考Hotspot术语表,
二、再说对象头的MarkWord
1. 32位虚拟机(了解)
2. 64位虚拟机(重要)
对象布局、GC回收和后面的锁升级就是对象标记MarkWord里面标志位的变化
三、分析Object obj = new Object()
1. JOL证明
1.1 JOL官网
http://openjdk.java.net/projects/code-tools/jol/
pom
<!--
官网:http://openjdk.java.net/projects/code-tools/jol/
定位:分析对象在JVM的大小和分布
-->
<dependency>
<groupId>org.openjdk.jol</groupId>
<artifactId>jol-core</artifactId>
<version>0.9</version>
</dependency>
1.2 测试案例
public class MyObject {
public static void main(String[] args){
//VM的细节详细情况
System.out.println(VM.current().details());
//所有的对象分配的字节都是8的整数倍。
System.out.println(VM.current().objectAlignment());
}
}
打印如下:
WARNING: Unable to get Instrumentation. Dynamic Attach failed. You may add this JAR as -javaagent manually, or supply -Djdk.attach.allowAttachSelf
Running 64-bit HotSpot VM.
Using compressed oop with 3-bit shift.
Using compressed klass with 0x0000000800000000 base address and 0-bit shift.
Objects are 8 bytes aligned.
Field sizes by type: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]
Array element sizes: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]
8
从日志可以看出,对象是 8 子节对齐的.
2. 案例2
public class MyObject {
public static void main(String[] args){
Object o = new Object();
System.out.println( ClassLayout.parseInstance(o).toPrintable());
}
}
打印如下:
属性 | 描述 |
---|---|
OFFSET | 偏移量,也就是到这个字段位置所占用的byte数 |
SIZE | 后面类型的字节大小 |
TYPE | 是Class中定义的类型 |
DESCRIPTION | DESCRIPTION是类型的描述 |
VALUE | VALUE是TYPE在内存中的值 |
3. 分代年龄
GC年龄采用4位bit存储,最大为15,例如 -XX:MaxTenuringThreshold=15
参数默认值就是15
如果设置参数超过15则会报错:
Error: Could not create the Java Virtual Machine.
Error: A fatal exception has occurred. Program will exit.
MaxTenuringThreshold of 16 is invalid; must be between 0 and 15
4. 参数说明
查看默认的参数
java -XX:+PrintCommandLineFlags -version
-XX:InitialHeapSize=532618688 -XX:MaxHeapSize=8521899008 -XX:+PrintCommandLineFlags -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:-UseLargePagesIndividualAllocation -XX:+UseParallelGC java version "1.8.0_241" Java(TM) SE Runtime Environment (build 1.8.0_241-b07) Java HotSpot(TM) 64-Bit Server VM (build 25.241-b07, mixed mode)
-XX:+UseCompressedClassPointers
表示默认开启了压缩指针, 因此对象头打印结果,类型指针不是理论上的8子节,变成了4子节.手动关闭压缩
-XX:-UseCompressedClassPointers
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 george_95@126.com