天天看點

5分鐘,帶你了解Java對象的記憶體布局

作者:Java機械師

前言

當你 new 一個對象時,如果你對它在記憶體中,到底什麼樣,究竟占多大記憶體感興趣。本文可以快速的給您答案。

文章中的例子,預設JVM為64位,無壓縮。

記憶體布局

Java記憶體布局 = 對象頭 + 執行個體資料 +對齊填充

圖檔講解

1.對象頭

對象頭由,markword+類型指針+數組長度組成。

普通對象:markword+類型指針 = 8位元組 + 4位元組 = 12位元組

數組對象:markword+類型指針 + 數組長度 = 8位元組 + 4位元組 +4位元組 = 16位元組

1.1 markword

在64位JVM中,markword是一個8位元組的資料。一位元組8位,是以markword是一個64位的資料。

在32位JVM中,結果不同,這裡不做贅述。

markword圖解

不同的位數,代表含義不同。這裡引用Mark Word 詳解文章中的圖檔。

5分鐘,帶你了解Java對象的記憶體布局

無鎖對象的markword

可能沒接觸過的同學看不懂該圖,我再舉個例子。我們都知道可以通過synchronized鎖對象,來做同步操作。

一個對象建立出來沒有被鎖過,它的markword應該是下圖這樣的。

5分鐘,帶你了解Java對象的記憶體布局

其餘鎖的狀态看法,可以參考上圖。

這裡需要注意,hashCode的值,隻有在調用hashcode()方法後,才會改變值。

1.2 類型指針

大小為4位元組,指向方法區中,該對象的類型。

1.3 數組長度

隻有當建立的是數組時,才有該部分,大小為4位元組,代表目前數組的長度。非數組時,不存在。

即:普通對象對象頭 = markword + 類型指針

數組對象 = markword + 類型指針 +數組長度

2.執行個體資料

一個對象的執行個體資料大小,等于它所有成員變量大小,以及繼承類的變量的大小的和。

如果你的對象不繼承任何對象,隻有一個int型變量。那麼你的執行個體資料大小就為4位元組。

如果對象A的父類有一個boolean類型變量,對象A有一個char類型變量。

A對象的執行個體資料大小 = boolean(1位元組)+char(2位元組)

無論父類的變量是private還是public修飾,都會算在子類的執行個體資料中。父類的父類也會算在執行個體資料中。

注意一點,靜态變量存放在方法區,是以不占用對象記憶體。

3.對齊填充

一個對象大小必須為8的整數倍。如果對象頭+執行個體資料 = 57個位元組。那麼對齊填充就為7位元組。對象頭+執行個體資料+對齊填充 = 64位元組。正好8的整數倍。你可以了解為對齊填充就是湊數用的。

執行個體講解

下面通過一個具體例子,以及運作結果,來印證下對象記憶體布局是否如上圖所說。

本文含有運作後的結果。大家可以直接通過代碼和結果來加深印象,無需實機運作代碼。

如何檢視對象

引入一個三方庫:jol,通過這個庫,可以列印對象記憶體布局。

implementation group: 'org.openjdk.jol', name: 'jol-core', version: '0.17'
複制代碼           

輔助類Student

/**
 * Author(作者):jtl
 * Date(日期):2023/3/26 19:20
 * Detail(詳情):學生類
 */
public class Student extends Person{
    String name = "ZhangSan";// 對象引用:4位元組
    boolean isRealMan;//boolean:1位元組
    int age = 10;//boolean:1位元組
    char score ='A';//char:2位元組
    long time = 20230326;//long:8位元組
    Student deskMate;//對象引用:4位元組

    public Student() {
    }

    static int grade;

    public boolean isRealMan() {
        return isRealMan;
    }

    public void setRealMan(boolean realMan) {
        isRealMan = realMan;
    }
}
複制代碼           

Student的父類Person

該類為了印證,建立Student對象時,Person類的所有變量(包括私有對象),都會占用Student對象的執行個體資料大小。

/**
 * Author(作者):jtl
 * Date(日期):2023/3/28 16:07
 * Detail(詳情):父類,Person類
 */
public class Person extends Object {
    private float time = 0L;
    String country = "China";
}
複制代碼           

實際運作代碼

這裡舉了5種例子,通過下文的運作結果,大家可以進行對比,加深印象。

public class Main {
    public static void main(String[] args) throws InterruptedException {
        Student student = new Student();
        System.out.println("-----------------------------第一階段 new 對象-------------------------------");
        System.out.println(ClassLayout.parseInstance(student).toPrintable());

        System.out.println("-----------------------------第二階段 hashcode-------------------------------");
        student.hashCode();
        System.out.println(ClassLayout.parseInstance(student).toPrintable());



        synchronized (student){
            System.out.println("-----------------------------第三階段 synchronized加鎖-------------------------------");
            System.out.println("synchronized (student):\n"+ClassLayout.parseInstance(student).toPrintable());
        }

        new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (student){
                    System.out.println("-----------------------------第四階段 synchronized加鎖-------------------------------");
                    System.out.println("new Thread:synchronized (student):\n"+ClassLayout.parseInstance(student).toPrintable());
                }
            }
        }).start();

        synchronized (student) {
            System.out.println("-----------------------------第五階段 Student[] 數組-------------------------------");
            System.out.println(ClassLayout.parseInstance(new Student[]{new Student(), new Student(), new Student()}).toPrintable());
        }
    }
}
複制代碼           

記憶體布局

這裡可以看到的是,隻有調用了hashcode方法後。markword中才會修改對應位置的資料。 另外隻有對象為數組時,才會有數組長度(array length) 這個選項,大小為4位元組。

> Task :Main.main()
-----------------------------第一階段 new 對象-------------------------------
org.example.Student object internals:
OFF  SZ                  TYPE DESCRIPTION               VALUE
  0   8                       (object header: mark)     0x0000000000000001 (non-biasable; age: 0)
  8   4                       (object header: class)    0x01000be8
 12   4                 float Person.time               0.0
 16   4      java.lang.String Person.country            (object)
 20   4                   int Student.age               10
 24   8                  long Student.time              20230326
 32   2                  char Student.score             A
 34   1               boolean Student.isRealMan         false
 35   1                       (alignment/padding gap)   
 36   4      java.lang.String Student.name              (object)
 40   4   org.example.Student Student.deskMate          null
 44   4                       (object alignment gap)    
Instance size: 48 bytes
Space losses: 1 bytes internal + 4 bytes external = 5 bytes total

-----------------------------第二階段 hashcode-------------------------------
org.example.Student object internals:
OFF  SZ                  TYPE DESCRIPTION               VALUE
  0   8                       (object header: mark)     0x00000073035e2701 (hash: 0x73035e27; age: 0)
  8   4                       (object header: class)    0x01000be8
 12   4                 float Person.time               0.0
 16   4      java.lang.String Person.country            (object)
 20   4                   int Student.age               10
 24   8                  long Student.time              20230326
 32   2                  char Student.score             A
 34   1               boolean Student.isRealMan         false
 35   1                       (alignment/padding gap)   
 36   4      java.lang.String Student.name              (object)
 40   4   org.example.Student Student.deskMate          null
 44   4                       (object alignment gap)    
Instance size: 48 bytes
Space losses: 1 bytes internal + 4 bytes external = 5 bytes total

-----------------------------第三階段 synchronized加鎖-------------------------------
synchronized (student):
org.example.Student object internals:
OFF  SZ                  TYPE DESCRIPTION               VALUE
  0   8                       (object header: mark)     0x000070000dcafac0 (thin lock: 0x000070000dcafac0)
  8   4                       (object header: class)    0x01000be8
 12   4                 float Person.time               0.0
 16   4      java.lang.String Person.country            (object)
 20   4                   int Student.age               10
 24   8                  long Student.time              20230326
 32   2                  char Student.score             A
 34   1               boolean Student.isRealMan         false
 35   1                       (alignment/padding gap)   
 36   4      java.lang.String Student.name              (object)
 40   4   org.example.Student Student.deskMate          null
 44   4                       (object alignment gap)    
Instance size: 48 bytes
Space losses: 1 bytes internal + 4 bytes external = 5 bytes total

-----------------------------第五階段 Student[] 數組-------------------------------
[Lorg.example.Student; object internals:
OFF  SZ                  TYPE DESCRIPTION               VALUE
  0   8                       (object header: mark)     0x0000000000000001 (non-biasable; age: 0)
  8   4                       (object header: class)    0x0101ed10
 12   4                       (array length)            3
 16  12   org.example.Student Student;.<elements>       N/A
 28   4                       (object alignment gap)    
Instance size: 32 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

-----------------------------第四階段 synchronized加鎖-------------------------------
new Thread:synchronized (student):
org.example.Student object internals:
OFF  SZ                  TYPE DESCRIPTION               VALUE
  0   8                       (object header: mark)     0x000060000265c002 (fat lock: 0x000060000265c002)
  8   4                       (object header: class)    0x01000be8
 12   4                 float Person.time               0.0
 16   4      java.lang.String Person.country            (object)
 20   4                   int Student.age               10
 24   8                  long Student.time              20230326
 32   2                  char Student.score             A
 34   1               boolean Student.isRealMan         false
 35   1                       (alignment/padding gap)   
 36   4      java.lang.String Student.name              (object)
 40   4   org.example.Student Student.deskMate          null
 44   4                       (object alignment gap)    
Instance size: 48 bytes
Space losses: 1 bytes internal + 4 bytes external = 5 bytes total
複制代碼           

上面結果的看法如下圖所示:

5分鐘,帶你了解Java對象的記憶體布局

另外,對齊填充大小如下圖所示。最終對象大小,一定是8的整數倍。

5分鐘,帶你了解Java對象的記憶體布局

至于 (alignment/padding gap) 什麼情況下會出現。本人沒有仔細研究,感興趣的同學可以看下jol的源碼。下圖為源碼中的具體位置。

結尾

至此,Java對象記憶體布局就講完了。不知道大家還記住多少呢。如果怕看了不久就忘了,可以收藏本文,點個贊支援下作者。文章中若有錯誤,可以及時指正。

今年Java找工作異常艱難,希望大家都可以度過這個寒冬。加油!!!

上文的示例代碼:Java記憶體布局 感興趣的同學,可以下載下傳運作一下。

繼續閱讀