java对象的内存布局

java对象内存布局简介

java对象的内存布局包含对象头、实例数据、对齐填充

对象头

  • markword:对象默认的hash码、分代年龄,锁的状态标识等。
  • class point:指向对象对应的类的元数据内存地址(堆指向方法区)。也就是指向Class的字节码。
  • length :数组对象持有的数组长度,只有数组特有。

markword

用于存储对象自身的运行时数据,如哈希码(HashCode)、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等等。

markword多长呢,是分虚拟机的。32位jvm的mark的长度是32,,4位jvm的markword的长度是64。具体存储的内容及内容长度如下:

32位jvm的markword存储:
在这里插入图片描述
64位jvm的markword存储:
在这里插入图片描述
上面的两个图片如何解析呢?
无锁状态下64位jvm前25bit没用到,存0;
接下来的31bit存对象的hashCode;
接下来的1bit未用到,存0;
接下来的4bit存分代年龄;
接下来的1bit存是否偏向锁;
接下来的2bit存锁标识位;
这样就知道64位的markword都存了什么东西。其他的锁状态分析与无锁状态类似。

指针

上面的markword存储的是运行时数据,比如这个对象的hashcode、锁的状态、垃圾回收信息等。
而指针中存储的的是指针,指针是干嘛的?指针是指向方法区的,说明这个对象是什么类型的。
64位jvm指针的长度是64bit。
需要注意的是,jdk1.8默认是开启压缩的,压缩完的指针就变成了32bit。可以通过参数-XX:-UseCompressedOops关闭指针压缩。

length

length中存储的是数组长度,64位jvm数组长度占32bit。

实例数据

主要是存放类的数据信息,父类的信息,对象字段属性信息。

如果对象有属性字段,则这里会有数据信息。如果对象无属性字段,则这里就不会有数据。根据字段类型的不同占不同的字节,例如boolean类型占1个字节,int类型占4个字节等等;

对齐填充

为了字节对齐,填充的数据。不是必须的,因为对齐了就不用填充了。

对象可以有对齐数据也可以没有。默认情况下,Java虚拟机堆中对象的起始地址需要对齐至8的倍数。如果一个对象用不到8N个字节则需要对其填充,以此来补齐对象头和实例数据占用内存之后剩余的空间大小。如果对象头和实例数据已经占满了JVM所分配的内存空间,那么就不用再进行对齐填充了。

所有的对象分配的字节总SIZE需要是8的倍数,如果前面的对象头和实例数据占用的总SIZE不满足要求,则通过对齐数据来填满。

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

汇总图

在这里插入图片描述

java对象的内存布局实例演示

下面写的测试代码主要打印两个对象的内存布局。一个是普通对象student,一个是数据对象strings。

代码如下:

  • 引入java内存对象的工具包
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>springcloud-demo</artifactId>
        <groupId>org.ludengke</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>study</artifactId>
<dependencies>

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

</project>
  • 测试类
package binary;

import org.openjdk.jol.info.ClassLayout;

/**
 * @author ludengke
 * @title: TestObjectStruct
 * @projectName springcloud-demo
 * @description: 测试java对象的内存布局
 * @date 2021/12/1922:07
 */
public class TestObjectStruct {
    static Student student = new Student();
    static String[] strings={"1","sdfsdf","232"};

    /**
     * 查看对象内部信息: ClassLayout.parseInstance(obj).toPrintable()
     * 查看对象外部信息:包括引用的对象:GraphLayout.parseInstance(obj).toPrintable()
     * 查看对象占用空间总大小:GraphLayout.parseInstance(obj).totalSize()
     * @param args
     */
    public static void main(String[] args) {
        System.out.println("student的结构:");
        System.out.println(student.hashCode());//对象的hashcode
        System.out.println(Integer.toHexString(student.hashCode()));//hashcode的16进制
        System.out.println(ClassLayout.parseInstance(student).toPrintable());//打印java对象内存布局
        System.out.println("strings的结构:");
        System.out.println(strings.hashCode());//对象的hashcode
        System.out.println(Integer.toHexString(strings.hashCode()));//hashcode的16进制
        System.out.println(ClassLayout.parseInstance(strings).toPrintable());//打印java对象内存布局

    }
}

  • 执行结果
    在这里插入图片描述

  • 结果解析

这张图是64位虚拟机,未进行指针压缩的执行结果。

图中输出了两个对象,一个是普通对象的student对象,一个是数组对象strings对象。数组对象中多了数组长度。

student对象的说明信息比较多,详细说明了对象头、实例数据、填充数据。可以由于图片篇幅有限,将前8位的说明画到string对象中。

为什么图片中的说明与上面《64位jvm的markword存储》图片的顺序不一致呢,正好是倒序?
这是涉及到一个知识点大端存储与小端存储,这里不详细叙述。我们这里之所以倒序,是因为大端存储的原因。

其它锁状态的验证有空再研究。

参考博客:
https://blog.youkuaiyun.com/baidu_28523317/article/details/104453927
https://www.cnblogs.com/jajian/p/13681781.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值