实验查看 Java 对象布局

前言

我们知道,对于普通的 Java 对象(非数组、非列表、非集合、非 Map 等对象),其内存布局由对象头实例数据对象补齐三部分组成,其布局如下图所示:
Java 对象布局图

其中,对象头MarkWorkClassPointer 组成。

MarkWork 用于存放 hashCodeGC 信息和锁信息,在 64JVM 中占用 8 个字节(无论是否开启压缩);

ClassPointer 用于存放对象类型,在 64JVM 中,不开启类型指针压缩占用 8 个字节,开启类型指针压缩后占用 4 个字节,默认开启类型指针压缩(-XX:+UseCompressedClassPointers);

下面通过实验的方式查看 Java 对象的布局。

环境准备

本次实验在 Debian 64 位操作系统中进行,使用 JDK 版本如下:

shaw@shaw-pc:~$ java -version
java version "1.8.0_211"
Java(TM) SE Runtime Environment (build 1.8.0_211-b12)
Java HotSpot(TM) 64-Bit Server VM (build 25.211-b12, mixed mode)

项目需要使用 JOL 工具,JOL 工具全称 Java Object Layout,用于分析 JVM 中的对象布局;

代码准备

  1. 引入 Maven 依赖

    <dependency>
        <groupId>org.openjdk.jol</groupId>
        <artifactId>jol-core</artifactId>
        <version>0.9</version>
    </dependency>
    
  2. UserInfo.java

    package com.shawearn.jvm.object.layout.model;
    
    /**
     * 用户信息;
     */
    public class UserInfo {
        private Integer userId;
        private String name;
    
        // 此处省略对应 get/set 方法;
    }
    
    
  3. Main.java

    package com.shawearn.jvm.object.layout;
    
    import com.shawearn.jvm.object.layout.model.UserInfo;
    import org.openjdk.jol.info.ClassLayout;
    
    public class Main {
        public static void main(String[] args) {
            Object object = new Object();
            System.out.println("========== Object ==========");
            System.out.println(ClassLayout.parseInstance(object).toPrintable());
            System.out.println("========== Object ==========");
    
            UserInfo userInfo = new UserInfo();
            System.out.println("========== userInfo ==========");
            System.out.println(ClassLayout.parseInstance(userInfo).toPrintable());
            System.out.println("========== userInfo ==========");
        }
    }
    
  4. 将上面代码打包成 jar 包,包文件命名:object-layout.jar

测试

1. 开启指针压缩(默认开启)

64 位的 JDK 默认会开启类指针压缩和对象指针压缩;

运行命令
# 运行 object-layout.jar 文件;
# 其中 -XX:+PrintCommandLineFlags 表示输出 JVM 采用的自动优化参数,此参数只是方便用户查看,运行时可不带此参数;
java -XX:+PrintCommandLineFlags -jar object-layout.jar
运行结果
-XX:InitialHeapSize=32652736 -XX:MaxHeapSize=522443776 -XX:+PrintCommandLineFlags -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseParallelGC 
========== Object ==========
java.lang.Object object internals:
 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           09 00 00 00 (00001001 00000000 00000000 00000000) (9)
      4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4        (object header)                           e5 01 00 20 (11100101 00000001 00000000 00100000) (536871397)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

========== Object ==========
========== userInfo ==========
com.shawearn.jvm.object.layout.model.UserInfo 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)                           f5 02 01 20 (11110101 00000010 00000001 00100000) (536937205)
     12     4   java.lang.Integer UserInfo.userId                           null
     16     4    java.lang.String UserInfo.name                             null
     20     4                     (loss due to the next object alignment)
Instance size: 24 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

========== userInfo ==========
说明

从上面的执行结果可以看到 JVM 的自动优化参数包含了如下两个:

-XX:+UseCompressedClassPointers 表示开启类指针压缩;

-XX:+UseCompressedOops 表示开启对象指针压缩;

可以看到 ObjectUserInfo 两个类对象大小分别为 16 bytes 和 24 bytes。

接下分析开启压缩时 ObjectUserInfo 两个类对象的内存信息。

Object

从上面的运行结果可知:

  • object 对象头(object header)占用 4 + 4 + 4 =12 个字节;
  • object 没有实例变量,所以实例数据占用 0 字节;
  • 由于 12 + 0 = 12,无法被 8 整除,所以对象补齐 4 字节(loss due to the next object alignment)。

因此整个 object 对象内存占用 12 + 0 + 4 = 16 字节;

UserInfo

由运行结果可知:

  • userInfo 对象头(object header)占用 4 + 4 + 4 =12 个字节;
  • 成员变量 (java.lang.Integer UserInfo.userId) 占用 4 个字节;
  • 成员变量 (java.lang.String UserInfo.name) 占用 4 个字节;
  • 由于 12 + 4 + 4 = 20 无法被 8 整除,所以对象补齐 4 字节(loss due to the next object alignment)。

因此整个 userInfo 对象内存占用 12 + 4 + 4 + 4 = 24 字节;

2. 关闭指针压缩

使用 -XX:-UseCompressedClassPointers-XX:-UseCompressedOops 两个参数分别关闭类指针压缩及对象指针压缩;

运行命令
# 运行 object-layout.jar 文件;
# 其中 -XX:+PrintCommandLineFlags 表示输出 JVM 采用的自动优化参数,此参数只是方便用户查看,运行时可不带此参数;
# -XX:-UseCompressedClassPointers 关闭类指针压缩;
# -XX:-UseCompressedOops 关闭对象指针压缩;
java -XX:+PrintCommandLineFlags -XX:-UseCompressedClassPointers -XX:-UseCompressedOops -jar object-layout.jar
运行结果
-XX:InitialHeapSize=32652736 -XX:MaxHeapSize=522443776 -XX:+PrintCommandLineFlags -XX:-UseCompressedClassPointers -XX:-UseCompressedOops -XX:+UseParallelGC 
========== Object ==========
java.lang.Object object internals:
 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           09 00 00 00 (00001001 00000000 00000000 00000000) (9)
      4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4        (object header)                           00 1c 46 48 (00000000 00011100 01000110 01001000) (1212554240)
     12     4        (object header)                           83 7f 00 00 (10000011 01111111 00000000 00000000) (32643)
Instance size: 16 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total

========== Object ==========
========== userInfo ==========
com.shawearn.jvm.object.layout.model.UserInfo 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)                           90 21 c1 48 (10010000 00100001 11000001 01001000) (1220616592)
     12     4                     (object header)                           83 7f 00 00 (10000011 01111111 00000000 00000000) (32643)
     16     8   java.lang.Integer UserInfo.userId                           null
     24     8    java.lang.String UserInfo.name                             null
Instance size: 32 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total

========== userInfo ==========
说明

从上面的执行结果可以看到已经关闭类指针压缩和对象指针压缩;

可以看到 ObjectUserInfo 两个类对象大小分别为 16 bytes 和 32 bytes。

乍眼一看,压缩前后 Object 对象的内存占用都是 16 字节,似乎没什么区别。其实不然,接下分析开启压缩时 ObjectUserInfo 两个类对象的内存信息。

Object

由运行结果可知:

  • object 对象头(object header)占用 4 + 4 + 4 + 4 =16 个字节;
  • object 没有实例变量,所以实例数据占用 0 字节;
  • 由于 16 + 0 = 16,可以被 8 整除,所以无需对象补齐。

因此整个 object 对象内存占用 16 + 0 = 16 字节;

UserInfo

由运行结果可知:

  • userInfo 对象头(object header)占用 4 + 4 + 4 + 4 =16 个字节;
  • 成员变量 (java.lang.Integer UserInfo.userId) 占用 8 个字节;
  • 成员变量 (java.lang.String UserInfo.name) 占用 8 个字节;
  • 由于 16 + 8 + 8 = 32,可以被 8 整除,所以无需对象补齐。

因此整个 userInfo 对象内存占用 16 + 8 + 8 = 32 字节;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值