天天看点

Java 对象的内存占用情况分析前言一、Java对象的内存布局二、JOL引入三、对象分析四、指针压缩总结

文章目录

  • 前言
  • 一、Java对象的内存布局
  • 二、JOL引入
  • 三、对象分析
    • 1.Object对象
    • 2.数组对象
    • 3.自定义对象
  • 四、指针压缩
  • 总结

前言

要想查看java对象在虚拟机的内存占用情况,我们可以使用OpenJDK官方提供的JOL(Java Object Layout)工具,即可很方便分析、了解一个Java对象在内存当中的具体布局情况。这里将在64位的HotSpot Java虚拟机环境下进行分析、测试

一、Java对象的内存布局

Java的实例对象、数组对象在内存中的组成包括如下三部分:对象头Hearder、实例数据、内存填充。示意图如下所示

Java 对象的内存占用情况分析前言一、Java对象的内存布局二、JOL引入三、对象分析四、指针压缩总结
  • 对象头:对象头主要包括两部分数据:Mark Word、Class对象指针,对于数组对象而言,其还包括了数组长度数据。在64位的HotSpot虚拟机下,Mark Word占8个字节,其记录了Hash Code、GC信息、锁信息等相关信息;而Class对象指针则指向该实例的Class对象,在开启指针压缩的情况下占用4个字节,否则占8个字节;如果其是一个数组对象,则还需要4个字节用于记录数组长度信息。
  • 实例数据:
  • 内存对齐:即内存填充,所有对象内存大小必须被8整除
Java 对象的内存占用情况分析前言一、Java对象的内存布局二、JOL引入三、对象分析四、指针压缩总结

二、JOL引入

引入最新版本的jol的maven依赖

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

三、对象分析

1.Object对象

代码如下(示例):

/**
         * java.lang.Object object internals:
         * OFF  SZ   TYPE DESCRIPTION               VALUE
         *   0   8        (object header: mark)     0x0000004f8e5cde01 (hash: 0x4f8e5cde; age: 0)
         *   8   4        (object header: class)    0xf80001e5 
         *  12   4        (object alignment gap)    对齐
         * Instance size: 16 bytes
         * Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
         */
        Object t = new Object();
        System.out.println(ClassLayout.parseInstance(t).toPrintable());
           

2.数组对象

/**
         * OFF  SZ   TYPE DESCRIPTION               VALUE
         *   0   8        (object header: mark)     0x0000000000000001 (non-biasable; age: 0)
         *   8   4        (object header: class)    0xf80001a9
         *  12   4        (array length)            2  数组长度
         *  12   4        (alignment/padding gap)   对齐
         *  16  16   long [J.<elements>             N/A 数组节点为long型,每个长度为8
         * Instance size: 32 bytes
         * Space losses: 4 bytes internal + 0 bytes external = 4 bytes total
         */
        long [] arryas = new long[2];
        System.out.println(ClassLayout.parseInstance(arryas).toPrintable());
           

3.自定义对象

用户实体信息

public class User {

    private String name;

    private int age;

    private Integer sex;

    private int [] arryas = new int[5];

    private long height;
}
           

对象内存信息

/**
         * OFF  SZ                TYPE DESCRIPTION               VALUE
         *   0   8                     (object header: mark)     0x0000000000000001 (non-biasable; age: 0)
         *   8   4                     (object header: class)    0xf80131f6
         *  12   4                 int User.age                  0 int类型4byte
         *  16   8                long User.height               0 long类型8byte
         *  24   4    java.lang.String User.name                 null 引用类型4byte
         *  28   4   java.lang.Integer User.sex                  null 引用类型4byte
         *  32   4               int[] User.arryas               [0, 0, 0, 0, 0] 引用类型4byte
         *  36   4                     (object alignment gap)  对齐
         * Instance size: 40 bytes
         * Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
         */
        User t = new User();
        System.out.println(ClassLayout.parseInstance(t).toPrintable());
           

由此可以得知各数据类型在分配内存大小的不同:

Java 对象的内存占用情况分析前言一、Java对象的内存布局二、JOL引入三、对象分析四、指针压缩总结

reference引用对象在32bit虚拟机占用4bytes,在64bit虚拟机上每个占用8bytes,如果开启指针压缩则每个占用4bytes。

四、指针压缩

在64位的HotSpot虚拟机下,类型指针、引用类型需要占8个字节。显然这大大增加了内存的消耗和占用。为此从JDK 1.6开始,64位的JVM支持UseCompressedOops选项。其可对OOP(Ordinary Object Pointer,普通对象指针)进行压缩,使其只占用4个字节,以达到节约内存的目的。

如果JVM的版本在 Java SE 6 update 23 及以上, 则不需要再设置 -XX:+UseCompressedOops 参数, 因为默认会开启。

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

总结

实际工作中真正需要手动计算对象大小的场景应该很少,但是个人觉得做为基础知识每个Java开发人员都应该了解,另外:对自己写的代码大概占用多少内存,内存中是怎么布局的应该有一个直觉性的认识。