文章目錄
- 前言
- 一、Java對象的記憶體布局
- 二、JOL引入
- 三、對象分析
-
- 1.Object對象
- 2.數組對象
- 3.自定義對象
- 四、指針壓縮
- 總結
前言
要想檢視java對象在虛拟機的記憶體占用情況,我們可以使用OpenJDK官方提供的JOL(Java Object Layout)工具,即可很友善分析、了解一個Java對象在記憶體當中的具體布局情況。這裡将在64位的HotSpot Java虛拟機環境下進行分析、測試
一、Java對象的記憶體布局
Java的執行個體對象、數組對象在記憶體中的組成包括如下三部分:對象頭Hearder、執行個體資料、記憶體填充。示意圖如下所示
- 對象頭:對象頭主要包括兩部分資料:Mark Word、Class對象指針,對于數組對象而言,其還包括了數組長度資料。在64位的HotSpot虛拟機下,Mark Word占8個位元組,其記錄了Hash Code、GC資訊、鎖資訊等相關資訊;而Class對象指針則指向該執行個體的Class對象,在開啟指針壓縮的情況下占用4個位元組,否則占8個位元組;如果其是一個數組對象,則還需要4個位元組用于記錄數組長度資訊。
- 執行個體資料:
- 記憶體對齊:即記憶體填充,所有對象記憶體大小必須被8整除
二、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());
由此可以得知各資料類型在配置設定記憶體大小的不同:
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開發人員都應該了解,另外:對自己寫的代碼大概占用多少記憶體,記憶體中是怎麼布局的應該有一個直覺性的認識。