一道阿里面試難題:如何計算JAVA物件大小?

程式設計師子牙發表於2021-12-25
在做JVM記憶體調優的時候,我們免不了需要去計算物件的大小。計算物件大小又要考慮是普通物件還是陣列物件,因為普通物件與陣列物件的物件頭存在些許差異。而且自JDK6以後,為了節省記憶體、提高執行效率,又引入了新的技術:指標壓縮。更加劇了計算物件大小的難度。

這篇文章就來深入分析如何計算物件大小,確保計算的結果與實際情況不差一個位元組。

物件結構

image.png

物件結構想必深入學習過JVM的人都知道,它分為三大部分:物件頭、例項資料、對齊填充。其中物件頭又分為三個部分:Mark Word、型別指標、陣列長度。其實物件頭還有第四部分,這是目前你看到的書、視訊都沒有提到的,物件頭也有對齊填充部分,這個部分也不是一定會有,只有陣列物件在未開啟指標壓縮的情況下才會出現。是不是一臉懵,那就繼續往後看。

指標壓縮

看到這四個字是不是一堆的問號:這是什麼?這怎麼實現的?為什麼說它節省了記憶體?……我們們就來搞清楚這些個問題。

我們們先達成觀念上的一致:所有的物件都是以8位元組對齊的。現在我有3個物件:test1(16位元組)、test2(32位元組)、test3(24位元組),為了便於說明,假如這三個物件中間沒有其他物件,那他們的記憶體地址是:

test1 = 0x0000 0 000(0位元組 ~ 16位元組) 
test2 = 0x0001 0 000(16位元組 ~ 48位元組) 
test3 = 0x0011 0 000(48位元組 ~ 72位元組)

大家發現規律了嗎,所有物件指標的後三位都是0,這就是指標壓縮的實現原理。開啟指標壓縮後,JVM會將物件指標的後三位給截去,如果test2 = 0x10000,在開啟指標後就變成了test2=0x10,在使用的時候將後三位的0補回去,即test2=0x10 000。

因為開啟了指標壓縮後,物件指標就變成了4位元組(32位),加上補3位,共35位。即開啟指標壓縮後一個物件指標能表示的最大堆空間是2的35次方,即32G。

那讀者可以想一想?如果我想擴充一個oop能表示的堆空間大小該怎麼做呢?

image.png

下面我們們來看四種情況(普通物件-關閉指標壓縮、普通物件-開啟指標壓縮、陣列物件-關閉指標壓縮、陣列物件-開啟指標壓縮)下的物件大小是如何計算出來的。建議讀者寫相似程式碼測試一下,這樣才能理解得更深刻。

測試程式碼:

package com.qimingnan.adjust;

import org.openjdk.jol.info.ClassLayout;

public class Test1 {
    int a = 10;
    int b = 20;

    static int[] arr = {0, 1, 2};

    public static void main(String[] args) {
        Test1 test1 = new Test1();

        System.out.printf(ClassLayout.parseInstance(test1).toPrintable());
        System.out.printf(ClassLayout.parseInstance(arr).toPrintable());
    }
}

普通物件

一、未開啟指標壓縮

24B = 8B(Mark Word)+ 8B(KClass Pointer)+ 4B + 4B

image.png

二、開啟指標壓縮

24B = 8B(Mark Word)+ 4B(KClass Pointer)+ 4B(int a)+ 4B(int b)+ 4B(Padding)

image.png

陣列物件

一、未開啟指標壓縮

40B = 8B(Mark Word)+ 8B(KClass Pointer)+ 4B(陣列長度)+ 4B(頭部Padding)+ 12B(3個int)+ 4B(物件Padding)

image.png

二、開啟指標壓縮

32B = 8B(Mark Word)+ 4B(KClass Pointer)+ 4B(陣列長度)+ 12B(3個int)+ 4B(物件Padding)

image.png

如何計算物件大小大家學會了嗎?

介紹一款工具,jol,大家在做測試的時候可以藉助這個工具

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

這篇文章有些地方可能不太好理解,讀者有問題的話,留言提問吧。我都會一一回復。

後續我將為大家新開《手寫JVM》的系列專欄,如果你對此感興趣的話,那麼就關注我吧。

相關文章