欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

实验查看 Java 对象布局

程序员文章站 2022-03-30 10:13:22
前言我们知道,Java 对象由对象头、实例数据、对象补齐三部分组成,其布局如下图所示:Java 对象布局图下面通过实验的方式查看 Java 对象的布局。环境准备本次实验在 Debian 64 位操作系统中进行,使用 JDK 版本如下:shaw@shaw-pc:~$ java -versionjava version "1.8.0_211"Java(TM) SE Runtime Environment (build 1.8.0_211-b12)Java HotSpot(TM) 64-Bit...

前言

我们知道,对于普通的 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 字节;

本文地址:https://blog.csdn.net/shawearn1027/article/details/107160537