首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java 對象記憶體大小計算規則與實戰演練

     編輯:Java 相關知識

對象佈局基礎(HotSpot 64位、開啟壓縮指針 -XX:+UseCompressedOops)

對象記憶體 = 對象頭 + 實例數據 + 對齊填充

1. 對象頭

  • Mark Word(8 bytes):HashCode、GC 分代年齡、鎖狀態標誌、偏向鎖線程 ID、時間戳等。
  • Klass Pointer(4 bytes,壓縮指針):指向類元數據的指針。
  • 數組長度(4 bytes):僅數組對象額外擁有。

普通對象頭 = 12 bytes,數組對象頭 = 16 bytes。

2. 實例數據存儲順序(默認分配策略)

  1. 長/雙:long、double(8 bytes)
  2. 整/浮:int、float(4 bytes)
  3. 短/字符:short、char(2 bytes)
  4. 位元組/布林:byte、boolean(1 byte)
  5. 引用:object reference(4 bytes,壓縮指針)

原則:同類型欄位聚合、寬類型優先、父類欄位在前、子類欄位在後。

3. 8 字節對齊

對象總大小必須是 8 的倍數,不足補齊(對齊填充)。


實戰計算演練

案例 1:基本欄位排序優化

// 未優化聲明順序
class BadOrder {
    byte a;      // 1
    int b;       // 4
    boolean c;   // 1
    long d;      // 8
    Object e;    // 4
}
// 計算:頭12 + a(1)+pad3 + b(4) + c(1)+pad7 + d(8) + e(4)+pad4 = 44 bytes

// 優化聲明順序(按分配策略排序)
class GoodOrder {
    long d;      // 8
    int b;       // 4
    Object e;    // 4
    byte a;      // 1
    boolean c;   // 1
}
// 計算:頭12 + d(8) + b(4) + e(4) + a(1) + c(1) + pad2 = 32 bytes
// 節省 12 bytes (27%)

案例 2:繼承關系佈局

class Parent {
    long a;   // 8
    int b;    // 4
    int c;    // 4
}
class Child extends Parent {
    long d;   // 8
}
// 佈局:頭12 + a(8) + b(4) + c(4) + d(8) = 36 → 對齊 40 bytes

案例 3:父類空間不足觸發緊湊佈局

class Parent {
    byte a;   // 1
}
class Child extends Parent {
    long b;   // 8
    short c;  // 2
    byte d;   // 1
}
// 父類佔用:頭12 + a(1) + pad3 = 16 bytes(已對齊)
// 子類接續:b(8) + c(2) + d(1) + pad5 = 16 bytes
// 總計:32 bytes

// 特殊規則:若子類首欄位為 long/double,且父類尾部有空閒空間(<8 bytes)
// JVM 會嘗試將子類的 long/double 壓入父類尾部空隙
class Parent2 {
    byte a;   // 1
}
class Child2 extends Parent2 {
    long b;   // 8 → 壓入父類 pad3 + pad4 = 7 bytes? 不夠,仍需新增 8 bytes
    short c;  // 2
    byte d;   // 1
}
// 實際佈局:頭12 + a(1) + c(2) + d(1) + pad2 + b(8) = 26 → 對齊 32 bytes
// 短類型 c/d 先填充父類空隙,long 獨佔 8 bytes

驗證工具:JOL (Java Object Layout)

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

// 使用
System.out.println(ClassLayout.parseClass(MyClass.class).toPrintable());

輸出示例:

MyClass object internals:
 OFFSET  SIZE  TYPE DESCRIPTION
      0    12  (object header)
     12     8  long MyClass.d
     20     4  int MyClass.b
     24     4  Object MyClass.e
     28     1  byte MyClass.a
     29     1  boolean MyClass.c
     30     2  (alignment padding)
Instance size: 32 bytes

進階主題

主題影響
關閉壓縮指針 (-XX:-UseCompressedOops)引用 8 bytes,對象頭 16 bytes,整體增大 30%~50%
32 位 JVM引用 4 bytes,對象頭 8 bytes (Mark Word 4 + Klass 4),無壓縮指針概念
逃逸分析 + 標量替換對象可能不在堆分配,而是拆解為棧上局部變數,大小為 0
@Contended緩存行填充,避免偽共享,每欄位額外 128 bytes

最佳實踐

  1. 高頻創建的對象(DTO、VO、Entity)按寬度排序欄位:long/double → int/float/ref → short/char → byte/boolean。
  2. 繼承鏈中,父類盡量填滿 8 字節倍數,避免子類長類型無法緊湊佈局。
  3. 大量對象場景(緩存、數據結構)考慮結構體化:用基本型別數組替代對象數組(如 long[] ids 替代 List)。
  4. 使用 JOL 定期審核核心類佈局,防止無意中字段順序變更導致記憶體暴漲。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved