天天看點

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開發人員都應該了解,另外:對自己寫的代碼大概占用多少記憶體,記憶體中是怎麼布局的應該有一個直覺性的認識。