Java對象內存結構詳解
Java對象在JVM內存中的存儲結構可以分為三個部分:對象頭(Header)、實例數據(Instance Data)和對齊填充(Padding)。以下是64位JVM(開啟壓縮指針)下的典型布局:
1. 對象頭(Header)
對象頭包含運行時元數據和控制信息,占12字節(壓縮指針)或16字節(未壓縮)
(1) Mark Word(8字節)
存儲對象自身的運行時數據,內容會隨鎖狀態變化:
鎖狀態 | 存儲內容 |
---|---|
無鎖 | 哈希碼(31bit) + 分代年齡(4bit) + 偏向模式(1bit) + 鎖標志(2bit) |
偏向鎖 | 線程ID(54bit) + Epoch(2bit) + 分代年齡(4bit) + 偏向模式(1bit) + 鎖標志(2bit) |
輕量級鎖 | 指向棧中鎖記錄的指針(62bit) + 鎖標志(2bit) |
重量級鎖 | 指向監視器(Monitor)的指針(62bit) + 鎖標志(2bit) |
GC標記 | 空(用于垃圾回收標記) |
(2) Klass Pointer(4字節)
指向方法區中的類元數據的指針(開啟壓縮指針時為4字節,否則8字節)
(3) 數組長度(可選,4字節)
如果是數組對象,額外存儲數組長度
2. 實例數據(Instance Data)
存儲對象真正的有效信息,即各個字段的內容,排列順序受以下規則影響:
- 基本類型優先(long/double → int/float → short/char → byte/boolean)
- 相同寬度字段放在一起
- 父類字段在子類之前
字段內存占用:
類型 | 大小 |
---|---|
boolean | 1字節 |
byte | 1字節 |
short | 2字節 |
char | 2字節 |
int | 4字節 |
float | 4字節 |
long | 8字節 |
double | 8字節 |
引用類型 | 4字節(壓縮指針)或8字節 |
示例:
class MyObject {byte b; // 1字節int i; // 4字節long l; // 8字節Object ref; // 4字節(壓縮指針)
}
// 實例數據總大小 = 1 + 4 + 8 + 4 = 17字節
3. 對齊填充(Padding)
JVM要求對象起始地址必須是8字節的整數倍,因此可能需要填充字節(0-7字節)
示例計算:
對象頭:12字節 (MarkWord 8 + KlassPointer 4)
實例數據:17字節
總計:12 + 17 = 29字節
需要填充到32字節(8的倍數)
最終對象大小:32字節
4. 完整對象內存布局示例
[對象頭][Mark Word(8字節)] 0x00000001d83eb950[Klass Pointer(4字節)] 0x0000000100000000[數組長度(4字節)] 0x00000005 (僅數組對象有)
[實例數據][int id(4字節)] 0x00000001[String name(4字節)] 0x000000076bdc8c18[double price(8字節)] 0x4024000000000000
[對齊填充(4字節)] 0x00000000
5. 查看對象內存布局的方法
(1) 使用JOL工具
// 添加依賴
<dependency><groupId>org.openjdk.jol</groupId><artifactId>jol-core</artifactId><version>0.16</version>
</dependency>// 打印對象布局
System.out.println(ClassLayout.parseInstance(obj).toPrintable());
(2) 示例輸出
class com.example.MyObject object internals:
OFF SZ TYPE DESCRIPTION VALUE0 8 (object header: mark) 0x00000001d83eb9508 4 (object header: class) 0x000000010000000012 4 int MyObject.id 116 8 double MyObject.price 10.024 4 java.lang.String MyObject.name "test"28 4 (object alignment gap)
Instance size: 32 bytes
6. 對象結構優化技巧
-
字段重排序:手動排列字段可減少填充
// 優化前:需要4字節填充 class Bad {byte b;long l;int i; } // 優化后:無填充 class Good {long l;int i;byte b; }
-
使用基本類型:避免包裝類(Integer等)的內存開銷
-
對象共享:對于不變對象可復用(如String池)
資料免費獲取