对象内存模型

创建对象的步骤

创建一个对象Object object = new Object();

从字节码角度


在这里插入图片描述

  • new:如果找不到Class实例对象,就会对该类进行加载。加载成功后,在堆中分配内存。内存分配完毕后,将指向这个实例对象的引入放入栈顶
  • dup:将栈顶的实例变量的引用复制一份,这时两个实例变量的引用指向了同一个对象实例。如构造方法()有参数,会将参数放入操作数栈中。
  • INVOKESPECIAL:用栈顶的引用,调用init<>的构造方法。



从执行步骤的角度

  1. 判断类是否被加载
  2. 在堆中分配内存地址
  3. 除了并发安全问题(TLAB、CAS)
  4. 对对象中的实例变量进行初始化。即设置默认值
  5. 设置对象的对象头
  6. 指向init<>方法进行初始化



从内存的角度

见jvm堆中对象的创建


对象的内存布局

对象的内存布局中,数组对象和一般对象的内存布局有点不一样,数组的内存布局多一个长度字段,其余的都是一样的。
图示:该图来源于网络
在这里插入图片描述

其中

  • MarkWord是对象中的一些信息,如hashCode、锁的类型、GC分代年龄
  • Klass Pointer的这个对象指向的类信息的指针
  • Instance Data:是这个对象中的实例变量数据
  • 对其填充的保证对象占用的是8的倍数的字节

对象头的说明:

在这里插入图片描述

因为对象头中包含锁的信息,而锁的信息的知识是JUC中的synchronized的知识,所以这里就关注第一个无锁的状态。
可以看到,对象头占用了68位bit,即8字节。在无锁情况下,有26位都是没有被使用的。

代码测试:
测试类:

public class User {

    int age = 10;
    boolean a = false;
    char c = 'a';
}

需要引入maven依赖:

        <!-- https://mvnrepository.com/artifact/org.openjdk.jol/jol-core -->
        <dependency>
            <groupId>org.openjdk.jol</groupId>
            <artifactId>jol-core</artifactId>
            <version>0.16</version>
        </dependency>

测试代码1:

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

输出:
在这里插入图片描述

可以看到,输出的结果以次是:对象头信息、类信息的指针、实例数据、字段补充
其中有一些不一样的信息:

  • 对象头信息的hashCode是为0,这是因为hashCode要在调用hashCode的时候才会被赋值
  • 类指针是占用的4字节,而不是8字节。主要是使用了指针压缩,可以使用-XX:-UseCompressedClassPointers来关闭指针压缩
  • 实例变量占用的字节和Java基础中描述的是一样的。但是其在内存被根据类型的大小对数据进行重排,这就可以提高内存的利用率,如图:

在这里插入图片描述

  • 补充位是将内存补充为8的整数被字节。这里补充到了24为。

测试代表2:调用了hashCode,并且关闭指针压缩-XX:-UseCompressedClassPointers

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

在这里插入图片描述

对象头的16进制为为:

0x0000006e0be85801 对应的二进制如下:
00000000 00000000 00000000 01101110 00001011 11101000 01011000 00000001
0x6e0be858 对应的二进制如下:
01101110 00001011 11101000 01011000

完成符合前面的概念,对象头也是一样的

宋红康老师的对象布局图

宋红康老师的对象布局图可以说是最详细的布局图,图如下:
在这里插入图片描述

对象的访问定位

即用哪种方式访问到对象的位置,有句柄访问直接使用指针访问

句柄访问

即通过一个中间状态来关联对象实例和对象的类
在这里插入图片描述

好处:在进行垃圾回收后,对象的移动只需要改变中间状态的指针即可,不用修改栈中的指针


直接使用指针访问

这是hosport虚拟机的访问方式,也是前面对象布局中的结果,直接将类型指针放在对象实例数据中
在这里插入图片描述

好处:好处:速度更快,java中对象访问频繁,每次访问都节省了一次指针定位的时间开销。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java对象内存模型包括对象头、实例数据和对齐填充三部分。对象头包含了关于堆对象的布局、类型、GC状态、同步状态和标识哈希码等基本信息。实例数据存放了类的数据信息、父类的信息以及对象字段属性信息。对齐填充主要是为了字节对齐而填充的数据,以凑齐8字节的倍数。 在JVM中,我们可以使用openjdk的jol工具来打印对象信息。通过调用`ClassLayout.parseInstance(object).toPrintable()`方法,我们可以打印出对象的信息。 例如,对于一个无属性的对象,打印的信息如下: ``` java.lang.Object object internals: OFFSET SIZE TYPE DESCRIPTION VALUE 0 4 (object header) 01 00 00 00 (00000001 00000000 00000000 00000000) (1) 4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0) 8 4 (object header) e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243) 12 4 (loss due to the next object alignment) Instance size: 16 bytes Space losses: 0 bytes internal + 4 bytes external = 4 bytes total ``` 这段信息描述了对象的各个部分。例如,对象头占据了前12个字节,分为三部分,每部分占4个字节。实例数据为空,所以没有具体的值。 这就是Java对象内存模型。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [Java对象内存模型](https://blog.csdn.net/u013190417/article/details/122532408)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值