new Object()到底占用几个字节,看完这篇就彻底明白了(转载)

前言

曾经面试被问题该问题了,当时肯定不会,印象特别深刻。今天看到本文章总结的很好,特意转载了。

对象的指向

先来看一段代码:

public class HeapMemory {
    private Object obj1 = new Object();

    public static void main(String[] args) {
        Object obj2 = new Object();
    }
}

上面的代码中,obj1 和obj2在内存中有什么区别?

方法区存储每个类的结构,比如:运行时常量池、属性和方法数据,以及方法和构造函数等数据。所以我们这个obj1是存在方法区的,而new会创建一个对象实例,对象实例是存储在堆内的,于是就有了下面这幅图(方法区指向堆):
在这里插入图片描述
而obj2 是属于方法内的局部变量,存储在Java虚拟机栈内的栈帧中的局部变量表内,这就是经典的栈指向堆:
在这里插入图片描述
这里我们再来思考一下,我们一个变量指向了堆,而堆内只是存储了一个实例对象,那么堆内的示例对象是如何知道自己属于哪个Class,也就是说这个实例是如何知道自己所对应的类元信息的呢?这就涉及到了一个Java对象在内存中是如何布局的。

Java内存模型

对象内存中可以分为三块区域:对象头(Header),实例数据(Instance Data)和对齐填充(Padding),以64位操作系统为例(未开启指针压缩的情况) Java对象布局如下图所示:
在这里插入图片描述
上图中的对齐填充不是一定有的,如果对象头和实例数据加起来刚好是8字节的倍数,那么就不需要对齐填充。

知道了Java内存布局,那么我们来看一个面试问题

Object obj=new Object()占用字节

这是网上很多人都会提到的一个问题,那么结合上面的Java内存布局,我们来分析下,以64位操作系统为例,new Object()占用大小分为两种情况:

  • 未开启指针压缩

    占用大小为:8(Mark Word)+8(Class Pointer)=16字节

  • 开启了指针压缩(默认是开启的)

    开启指针压缩后,Class Pointer会被压缩为4字节,最终大小为:

    8(Mark Word)+4(Class Pointer)+4(对齐填充)=16字节

结果到底是不是这个呢?我们来验证一下。
首先引入一个pom依赖:

<dependency>
       <groupId>org.openjdk.jol</groupId>
       <artifactId>jol-core</artifactId>
       <version>0.10</version>
</dependency>

然后新建一个简单的demo:

public class HeapMemory {
    public static void main(String[] args) {
        Object obj = new Object();
        System.out.println(ClassLayout.parseInstance(obj).toPrintable());
    }
}

输出结果如下:
在这里插入图片描述
最后的结果是16字节,没有问题,这是因为默认开启了指针压缩,那我们现在把指针压缩关闭之后再去试试。

-XX:+UseCompressedOops  开启指针压缩
-XX:-UseCompressedOops  关闭指针压缩

 

在这里插入图片描述
再次运行,得到如下结果:
在这里插入图片描述
可以看到,这时候已经没有了对齐填充部分了,但是占用大小还是16位。

下面我们再来演示一下如果一个对象中带有属性之后的大小。

新建一个类,内部只有一个byte属性:

public class MyItem {
    byte i = 0;
}

然后分别在开启指针压缩和关闭指针压缩的场景下分别输出这个类的大小。

import org.openjdk.jol.info.ClassLayout;

public class HeapMemory {
    public static void main(String[] args) {
        MyItem myItem = new MyItem();
        System.out.println(ClassLayout.parseInstance(myItem).toPrintable());
    }
}

 

开启指针压缩,占用16字节:
在这里插入图片描述
关闭指针压缩,占用24字节:
在这里插入图片描述
这个时候就能看出来开启了指针压缩的优势了,如果不断创建大量对象,指针压缩对性能还是有一定优化的。

对象的访问

创建好一个对象之后,当然需要去访问它,那么当我们需要访问一个对象的时候,是如何定位到对象的呢?
目前最主流的访问对象方式有两种:句柄访问和直接指针访问。

  • 句柄访问

    使用句柄访问的话,Java虚拟机会在堆内划分出一块内存来存储句柄池,那么对象当中存储的就是句柄地址,然后句柄池中才会存储对象实例数据和对象类型数据地址。

    在这里插入图片描述

  • 直接指针访问(Hot Spot虚拟机采用的方式)

    直接指针访问的话对象中就会直接存储对象类型数据。

句柄访问和直接指针访问对比

上面图形中我们很容易对比,就是如果使用句柄访问的时候,会多了一次指针定位,但是他也有一个好处就是,假如一个对象被移动(地址改变了),那么只需要改变句柄池的指向就可以了,不需要修改reference对象内的指向,而如果使用直接指针访问,就还需要到局部变量表内修改reference指向。

转载地址:https://blog.csdn.net/zwx900102/article/details/108108555

posted @ 2021-02-23 14:41  蜗牛学编程  阅读(377)  评论(0编辑  收藏  举报