Object o = new Object()占多少个字节?-对象的内存布局

一、先上答案

这个问题有坑,有两种回答

第一种解释:

object实例对象,占16个字节。

第二种解释:

Object o:普通对象指针(ordinary object pointer),占4个字节。
new Object():object实例对象,占16个字节。
所以一共占:4+16=20个字节。

第二种解释就是在玩文字游戏了,但还是要知道的。

二、这个答案适用于所有情况吗

并不是,这个答案只适用于现在一般默认情况。

准确的说,只适用于 HotSpot实现64位虚拟机,默认开启了 压缩类指针压缩普通对象指针的情况下。

本文下述内容若无特殊说明,指的都是 JDK8 HotSpot实现64位虚拟机的 未开启压缩的情况。

三、前置知识

在 JVM 中,Java对象保存在堆中时,由以下三部分组成:

  • 对象头(Object Header):包括关于堆对象的布局、类型、GC状态、同步状态和标识哈希码的基本信息。由两个词 mark wordklass pointer组成,如果是数组对象的话,还会有一个 length field
  • mark word:通常是一组位域,用于存储对象自身的运行时数据,如hashCode、GC分代年龄、锁同步信息等等。占用64个比特,8个字节。
  • klass pointer:类指针,是对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例。占用64个比特,8个字节。开启压缩类指针后,占用32个比特,4个字节。
  • 实例数据(Instance Data):存储了代码中定义的各种字段的内容,包括从父类继承下来的字段和子类中定义的字段。如果对象无属性字段,则这里就不会有数据。根据字段类型的不同占不同的字节,例如boolean类型占1个字节,int类型占4个字节等等。为了提高存储空间的利用率,这部分数据的存储顺序会受到虚拟机分配策略参数和字段在Java源码中定义顺序的影响。
  • 对齐填充(Padding):对象可以有对齐数据也可以没有。默认情况下,Java虚拟机堆中对象的起始地址需要对齐至8的整数倍。如果一个对象的对象头和实例数据占用的总大小不到8字节的整数倍,则以此来填充对象大小至8字节的整数倍。

为什么要对齐填充?字段内存对齐的其中一个原因,是让字段只出现在同一CPU的缓存行中。如果字段不是对齐的,那么就有可能出现跨缓存行的字段。也就是说,该字段的读取可能需要替换两个缓存行,而该字段的存储也会同时污染两个缓存行。这两种情况对程序的执行效率而言都是不利的。其实对其填充的最终目的是为了计算机高效寻址。

Object o = new Object()占多少个字节?-对象的内存布局

我看到网络上有些文章把 mark word称之为对象头,把java对象的内存布局分为4个部分mark word、klass pointer、instance data、padding,这很明显是没有看过官方文档的,说法并不严谨。关于对象头,可以在HotSpot官方文档找到下面的描述:

Object o = new Object()占多少个字节?-对象的内存布局

四、详细解释

因为第二种解释包含了第一种解释,所以我们分析第二种解释。

1.Object o

在HotSpot实现的64位虚拟机中,原本情况下,它内部的一个引用,就应该占64个比特,也就是8个字节。什么叫引用啊?上面那个变量小o,就叫引用,也叫普通对象指针(别说什么java里没有指针,什么引用和指针不一样。我不想去争论这个)。但是,在第二种解释中我们说了,普通对象指针,占4个字节,怎么又成8个字节了,怎么回事呢?

这是因为 HotSpot实现的64位虚拟机,默认会开启压缩普通对象指针,会把8个字节的对象引用,压缩成4个字节。

Object o占用大小分为两种情况:

  • 未开启压缩对象指针 8字节
  • 开启压缩对象指针(默认是开启的) 4字节

2.new Object()

同样的,在HotSpot实现的64位虚拟机中,原本情况下,类指针应该占64个比特,也就是8个字节。但因为 HotSpot实现的64位虚拟机,默认会开启压缩类指针(和压缩对象指针不一样),而类指针就在 Klass Pointer中存储着,所以会把 Klass Pointer压缩成4个字节。

new Object()占用大小分为两种情况:

  • 未开启压缩类指针 8字节(Mark Word) + 8字节(Klass Pointer) = 16字节
  • 开启压缩类指针(默认是开启的) 8字节(Mark Word) + 4字节(Klass Pointer) + 4字节(Padding) = 16字节

五、验证

光说不练假把式,实践出真知,上面的只是理论,我们来实际验证下,是不是真的是这样。

1.验证默认开启压缩

首先,我们来看下, JDK8 HotSpot实现64位虚拟机,是不是会默认开启 压缩类指针压缩普通对象指针

win + R,输入 cmd,敲入下面的命令 java -version,相信大家对这个命令很熟悉了,查看java版本

Object o = new Object()占多少个字节?-对象的内存布局

接下来我们加个参数 -XX:+PrintCommandLineFlags,这个参数让JVM打印出那些已经被用户或者JVM设置过的详细的XX参数的名称和值,注意看下面两个参数

Object o = new Object()占多少个字节?-对象的内存布局

-XX:+UseCompressedClassPointers:使用 压缩类指针

-XX:+UseCompressedOops:使用 压缩普通对象指针

可以看到,这两个配置是默认开启的。

注意: 32位HotSpot VM是不支持 UseCompressedOops 参数的,只有64位HotSpot VM才支持。

什么是oop?

这参数后面的oop可不是面向对象编程Object Oriented Programming的意思,而是普通对象指针Ordinary Object Pointer。

启用 UseCompressedOops后,会压缩的对象:

  • 每个Class的属性指针(静态成员变量);
  • 每个对象的属性指针;
  • 普通对象数组的每个元素指针。

当然,压缩也不是所有的指针都会压缩,对一些特殊类型的指针,JVM是不会优化的,例如指向PermGen的Class对象指针、本地变量、堆栈元素、入参、返回值和NULL指针不会被压缩。

关于UseCompressedClassPointers和UseCompressedOops

这样一看,好像UseCompressedOops对Object的内存布局并没有影响,其实不然,开启UseCompressedOops,默认会开启UseCompressedClassPointers,会压缩klass pointer这部分的大小,由8字节压缩至4字节,间接的提高内存的利用率。关闭UseCompressedOops默认会关闭UseCompressedClassPointers。

如果开启类指针压缩,+UseCompressedClassPointers,并关闭普通对象指针压缩,-UseCompressedOops,此时会报警告,”Java HotSpot(TM) 64-Bit Server VM warning: UseCompressedClassPointers requires UseCompressedOops”。因为UseCompressedClassPointers的开启是依赖于UseCompressedOops的开启。

总结下就是,开了UseCompressedOops,UseCompressedClassPointers可开可不开,默认会也被打开。关了UseCompressedOops,UseCompressedClassPointers开了会报警告,默认会也被关掉。这两个配置,在不特意修改的情况下都是默认开启的。

2.验证实例对象布局大小

上面已经看到,JVM默认开启了 压缩类指针压缩普通对象指针,那么在这个情况下,new Object()是否真的是8字节(Mark Word) + 4字节(Klass Pointer) + 4字节(Padding) = 16字节呢?

还好 openjdk 给我们提供了一个工具包,可以用来获取对象的信息和虚拟机的信息,我们只需引入 jol-core 依赖,如下:


    org.openjdk.jol
    jol-core
    0.14

jol-core 常用的三个方法:

  • ClassLayout.parseInstance(object).toPrintable():查看对象内部信息.

  • GraphLayout.parseInstance(object).toPrintable():查看对象外部信息,包括引用的对象.

  • GraphLayout.parseInstance(object).totalSize():查看对象总大小.

简单对象

为了简单化,我们不用复杂的对象,自己创建一个类 Test01,先看无属性字段的时候

public class Test01 {

    public static void main(String[] args) {
        Test01 t = new Test01();
        System.out.println(ClassLayout.parseInstance(t).toPrintable());
    }

}

通过 jol-core 的 api,我们将对象的内部信息打印出来:

Object o = new Object()占多少个字节?-对象的内存布局

可以看到有 OFFSET、SIZE、TYPE DESCRIPTION、VALUE 这几个名词头,它们的含义分别是

  • OFFSET:偏移地址,单位字节;
  • SIZE:占用的内存大小,单位为字节;
  • TYPE DESCRIPTION:类型描述,其中object header为对象头;
  • VALUE:对应内存中当前存储的值,二进制32位;

同时可以看到,t实例对象共占据16Byte,object header占据12Byte,其中mark word占8Byte,klass pointer占4Byte,还有padding占4Byte。

如果我把 压缩类指针的参数去掉呢?可以通过配置vm参数关闭压缩类指针, -XX:-UseCompressedClassPointers。我们再看看结果:

Object o = new Object()占多少个字节?-对象的内存布局

Object o = new Object()占多少个字节?-对象的内存布局

可以看到,t实例对象还是占据16Byte,但object header所占用的内存大小变为16Byte,其中mark word占8Byte,klass pointer占8Byte,无padding。

klass pointer的大小从上面的4Byte,变成了8Byte,正是因为没有对它进行压缩。同时也因为对象大小已经达到16Byte,是8的整数倍,所以不再需要padding。

至此,已经证明了我们上面的结论是正确的。

有成员变量的对象

我们现在给Test01类里加4个成员变量,开启两个指针压缩,再看看它的布局吧

public class Test01 {

    String a = "a";

    int b = 1;

    boolean c = false;

    char d = 'd';

    public static void main(String[] args) {
        Test01 t = new Test01();
        System.out.println(ClassLayout.parseInstance(t).toPrintable());
    }

}

Object o = new Object()占多少个字节?-对象的内存布局

可以看到,对象大小变成了24Byte,其中mark word占8Byte,klass pointer占4Byte,int占4Byte,char占2Byte,boolean占1Byte,padding占1Byte,String类型的变量a占4Byte,也验证了我们上面说的”为了提高存储空间的利用率,这部分数据的存储顺序会受到虚拟机分配策略参数和字段在Java源码中定义顺序的影响”,可以看到内存中的布局顺序确实和我们定义的不一样。

此时我再关闭两个指针压缩,再看看布局变化:

Object o = new Object()占多少个字节?-对象的内存布局

Object o = new Object()占多少个字节?-对象的内存布局

可以看到,对象总大小变成了32Byte,和开启压缩类指针相比,klass pointer大了4Byte,和开启压缩普通对象指针相比,String类型的变量a大了4Byte。符合我们上面的结论。

Original: https://www.cnblogs.com/dijia478/p/14677243.html
Author: dijia478
Title: Object o = new Object()占多少个字节?-对象的内存布局

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/593821/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • JavaSE前期准备3

    修饰符 成员方法 构造方法 成员变量 局部变量 abstract(抽象的) static (静态的) public(公共的) protected(受保护的) private(私有的…

    Java 2023年6月5日
    086
  • idea创建类报错:This template did not produce a Java class

    在idea.exe.vmoptions 或 idea64.exe.vmoptions中加入配置-Djdk.util.zip.ensureTrailingSlash=false重启,…

    Java 2023年5月29日
    065
  • 库存管理系统实现 C语言课设

    1 #include 2 #include 3 #include <string.h> 4 5 //定义一个&#…

    Java 2023年6月5日
    078
  • Hibernate知识总结(一)

    一、ORM ORM的全称是Object/Relation Mapping,即对象/关系映射,可以将其理解成一种规范,它概述了这类框架的基本特征:完成面向对象的编程语言到关系数据库的…

    Java 2023年6月6日
    0104
  • 动态规划:LeetCode.H0629.K个逆序对数组

    给出两个整数 n 和 k,找出所有包含从 1 到 n 的数字,且恰好拥有 k 个逆序对的不同的数组的个数。 逆序对的定义如下:对于数组的第i个和第 j个元素,如果满i < j…

    Java 2023年6月7日
    0122
  • 第一周

    第一周 service层,mapper接口,实体类 本次问题:外键表中不存在对应的值,所以报错 使用mybatis plus的动态拼接sql出错 (1) char长度固定, 即每条…

    Java 2023年6月7日
    071
  • Spring Boot:实现MyBatis动态数据源

    综合概述 在很多具体应用场景中,我们需要用到动态数据源的情况,比如多租户的场景,系统登录时需要根据用户信息切换到用户对应的数据库。又比如业务A要访问A数据库,业务B要访问B数据库等…

    Java 2023年5月30日
    061
  • 关于Request复用的那点破事儿。研究明白了,给你汇报一下。

    你好呀, 我是歪歪。 说的是由于 Request 在 tomcat 里面是复用的,所以如果在一个 Request 的生命周期完成之后,在异步线程里面调用了相关的方法,会导致这个 R…

    Java 2023年6月5日
    0101
  • 消息队列MQ核心原理全面总结(11大必会原理)

    消息队列已经逐渐成为分布式应用场景、内部通信、以及秒杀等高并发业务场景的核心手段,它具有低耦合、可靠投递、广播、流量控制、最终一致性 等一系列功能。 无论是 RabbitMQ、Ro…

    Java 2023年6月15日
    090
  • 基于UML软件建模的在线学习平台

    开发背景与意义 随着教育事业的改革,国家对高等教学逐渐得到重视。但是传统的高校间教学教育无法在学生与老师之间形成反馈,还是停留在简单的知识共享的初级阶段,往往不能达到教学目的。而传…

    Java 2023年6月5日
    081
  • Spring总结

    **Spring&#x7684;&#x4E2A;&#x4EBA;&#x603B;&#x7ED3;&#x548C;&#x914…

    Java 2023年6月5日
    087
  • linux指定端口失效原因分析

    在搭建centos7系统后开启防护墙,防火墙状态为active(开启), firewall-cmd –zone=public –add-port=8080/…

    Java 2023年6月14日
    099
  • Java8 函数式【1】:一文读懂逆变

    Java8 函数式【1】:一文读懂逆变 禁止转载 pure function 协变 逆变 Java8 引入了函数式接口,从此方法传参可以传递函数了,有人说:不就是传一个方法吗,语法…

    Java 2023年6月16日
    092
  • Spring的自动装配和注解

    自动装配是使用spring满足bean依赖的一种方法 spring会在应用上下文中为某个bean寻找其依赖的bean。 Spring的自动装配需要从两个角度来实现,或者说是两个操作…

    Java 2023年6月15日
    069
  • java面试——流

    流是一组有顺序的,有起点和终点的字节集合,是对数据传输的总称或抽象。即数据在两设备间的传输称为流,流的本质是数据传输,根据数据传输特性将流抽象为各种类,方便更直观的进行数据操作。 …

    Java 2023年6月9日
    095
  • 线程池线程复用的原理

    前言 线程池最大的作用就是复用线程。在线程池中,经过同一个线程去执行不一样的任务,减少反复地创建线程带来的系统开销,就是线程的复用。那么线程池线程复用的原理是什么呢? 之前面试被问…

    Java 2023年6月5日
    078
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球