-Xmx、-Xms、新生代/老年代比例。+------------------+------------------+------------------+ | Object Header | Instance Data | Padding | | (12 bytes) | (Fields) | (0-7 bytes) | +------------------+------------------+------------------+ 對齊要求:總大小為 8 字節倍數
普通對象頭 = 12 bytes;數組對象頭 = 16 bytes(開啟壓縮指針)。
| 類型 | 大小 | 備註 |
|---|---|---|
| boolean, byte | 1 byte | |
| char, short | 2 bytes | |
| int, float | 4 bytes | |
| long, double | 8 bytes | |
| reference (對象引用) | 4 bytes | 開啟壓縮指針;關閉則 8 bytes |
static 字段不計入實例大小(屬於類元數據區)。對象起始地址 8 字節對齊,總大小補齊至 8 的倍數。Padding = (8 - (Header + Data) % 8) % 8。
// SizeOfAgent.java
package com.example.jvm;
import java.lang.instrument.Instrumentation;
public class SizeOfAgent {
private static Instrumentation inst;
public static void premain(String args, Instrumentation instP) {
inst = instP;
}
// 淺層大小(不含引用對象)
public static long sizeOf(Object o) {
if (inst == null) throw new IllegalStateException("Agent not initialized");
return inst.getObjectSize(o);
}
// 深層大小(遞歸計算引用鏈,簡易版)
public static long fullSizeOf(Object o) {
// 完整實現需配合反射遍歷字段、處理循環引用、數組展開,此處略
return sizeOf(o); // 佔位
}
}
Manifest-Version: 1.0
Premain-Class: com.example.jvm.SizeOfAgent
Can-Redefine-Classes: false
Can-Retransform-Classes: false
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.2.0</version>
<configuration>
<archive>
<manifestEntries>
<Premain-Class>com.example.jvm.SizeOfAgent</Premain-Class>
<Can-Redefine-Classes>false</Can-Redefine-Classes>
</manifestEntries>
</archive>
</configuration>
</plugin>
java -javaagent:target/sizeof-agent.jar -XX:+UseCompressedOops -cp target/classes com.example.jvm.SizeOfAgentTest
public class SizeOfAgentTest {
static class A { int a; Integer b; }
static class B { int a; Integer b; }
static class C { int c; B[] b = new B[2]; { b[0]=new B(); b[1]=new B(); } }
static class D { byte d1; }
static class E extends D { byte e1; }
public static void main(String[] args) {
System.out.println("=== 基礎對象 ===");
System.out.println("new Object() : " + SizeOfAgent.sizeOf(new Object())); // 16
System.out.println("new A() : " + SizeOfAgent.sizeOf(new A())); // 24 (header12 + int4 + ref4 + pad4)
System.out.println("new Integer(1) : " + SizeOfAgent.sizeOf(new Integer(1))); // 24
System.out.println("new String() : " + SizeOfAgent.sizeOf(new String())); // 32
System.out.println("new String(\"a\") : " + SizeOfAgent.sizeOf(new String("a"))); // 32
System.out.println("\n=== 數組對象 ===");
System.out.println("new byte[0] : " + SizeOfAgent.sizeOf(new byte[0])); // 16 (header16)
System.out.println("new byte[1] : " + SizeOfAgent.sizeOf(new byte[1])); // 24 (header16 + 1 + pad7)
System.out.println("new byte[9] : " + SizeOfAgent.sizeOf(new byte[9])); // 32 (header16 + 9 + pad7)
System.out.println("new Integer[0] : " + SizeOfAgent.sizeOf(new Integer[0])); // 16
System.out.println("new Integer[3] : " + SizeOfAgent.sizeOf(new Integer[3])); // 32 (header16 + 3*4 + pad4)
System.out.println("\n=== 繼承與復合 ===");
System.out.println("new D() : " + SizeOfAgent.sizeOf(new D())); // 16 (header12 + byte1 + pad3)
System.out.println("new E() : " + SizeOfAgent.sizeOf(new E())); // 24 (header12 + byte1(pad3) + byte1 + pad2)
System.out.println("new C() : " + SizeOfAgent.sizeOf(new C())); // 32 (淺層:header12 + int4 + ref4 + pad4)
}
}
new Object() = 16:Header 12 + Padding 4 = 16(對齊)。new A() = 24:Header 12 + int(4) + ref(4) = 20 → Padding 4 = 24。Integer 內部 int value + Header = 16,但對象引用指向它佔 4 字節。org.openjdk.jol:jol-core,提供 ClassLayout.parseClass(Foo.class).toPrintable() 詳細佈局輸出,無需 Agent。heapdump + jhat/MAT 離線分析。-XX:+PrintGCDetails -XX:+PrintHeapAtGC 間接推算。Java 對象內存佈局遵循確定性規則:Header + Instance Data + Padding。掌握壓縮指針、字段重排、對齊填充原理,配合 JOL 或 Instrumentation Agent 工具化測量,能將內存規劃從「經驗拍腦袋」轉為「數據驅動」,是 JVM 調優進階的必修課。