JVM 將內存劃分為多個區域,其中 Stack(棧/虛擬機棧) 與 Heap(堆) 是開發者最常打交道的兩大核心區域。
| 維度 | Stack (虛擬機棧) | Heap (堆) |
|---|---|---|
| 存儲內容 | 基本類型變量、對象引用、方法調用幀、局部變量表、操作數棧 | 所有 new 出來的對象實例、數組、String 常量池(JDK7+) |
| 線程私有性 | 線程私有,生命週期與線程同步 | 線程共享,全局唯一 |
| 分配方式 | 棧幀隨方法調用壓棧、返回出棧,自動釋放 | 對象在堆分配,由 GC 回收 |
| 訪問速度 | 極快(指針移動) | 較慢(需 GC 管理、可能觸發 Young/Full GC) |
| 溢出異常 | StackOverflowError(遞歸過深、大局部變量) | OutOfMemoryError: Java heap space |
| 典型大小 | -Xss 設置(默認 1MB) | -Xms/-Xmx 設置(默認物理記憶體 1/64 ~ 1/4) |
public class MemoryDemo {
public static void main(String[] args) { // 1. main 棧幀入棧
int a = 10; // 2. 基本類型直接在棧分配
Person p = new Person("Alice", 25); // 3. 引用 p 在棧,對象在堆
p.sayHello(); // 4. 調用方法,新棧幀壓棧
}
}
class Person {
String name; // 引用類型,指向堆中 String 對象
int age; // 基本類型,若為實例字段則在堆對象內部
Person(String name, int age) {
this.name = name;
this.age = age;
}
void sayHello() {
String msg = "Hello " + name; // msg 引用在棧,字符串對象在堆
System.out.println(msg);
}
}
關鍵點:Person p 是棧上的引用變量(4/8 字節指針),new Person() 創建的對象實體在堆上。方法參數傳遞的是引用的副本(值傳遞)。
自動釋放不再被引用的堆對象佔用的內存,防止 OOM,提高內存利用率。
無法解決循環引用問題,JDK 主流實現未採用。
| 引用類型 | 強度 | GC 行為 | 典型場景 |
|---|---|---|---|
| 強引用 | 最強 | 永不回收(寧可 OOM) | Object obj = new Object() |
| 軟引用 | 次強 | 內存不足時回收 | 緩存 |
| 弱引用 | 較弱 | 下次 GC 必回收 | WeakHashMap、監聽器 |
| 虛引用 | 最弱 | 無法單獨獲取對象,配合 ReferenceQueue 跟蹤回收 | DirectBuffer 清理 |
串行:Serial (新生代複製) + Serial Old (老年代標記-整理) -> 單核 Client 模式
並行:Parallel Scavenge + Parallel Old -> 吞吐量優先,多核 Server
並發:CMS (老年代並發標記-清除) + ParNew (新生代並行複製) -> 低延遲,但碎片化、CPU 敏感
區域化:G1 (Region-based, 混合收集) -> 大堆、可預測停頓,JDK9+ 默認
新一代:ZGC / Shenandoah (彩色指針/讀屏障, 亞毫秒級停頓) -> TB 級大堆,超低延遲
-XX:PretenureSizeThreshold)-XX:MaxTenuringThreshold,默認 15)obj = null,縮短強引用鏈(特別是長生命周期集合、緩存)-Xms -Xmx -Xmn -XX:SurvivorRatio -XX:+UseG1GC 等-Xlog:gc*:file=gc.log:time,uptime,level,tags(JDK9+)WeakHashMap、Guava Cache、Caffeine誤區 1:基本類型都在棧上。
✅ 修正:局部變量基本類型在棧;實例字段基本類型隨對象在堆;static 基本類型在方法區。誤區 2:String 常量池在堆。
✅ 修正:JDK 7 前在永久代(方法區),JDK 7+ 移至堆。誤區 3:System.gc() 能保證立即回收。
✅ 修正:只是建議,JVM 可忽略。
理解 Stack 存引用/局部變量、Heap 存對象實體 是分析內存問題的基石;掌握 分代收集、可達性分析、引用類型、主流收集器特點 能指導 JVM 調優與排查 OOM。實戰中:代碼層面減少強引用持有時間,配置層面選對收集器並壓測驗證,才是高效內存管理之道。