理解棧與堆是掌握 Java 內存管理、排查 OOM、優化性能的基石。本文從內存劃分、存儲內容、訪問速度、生命周期、垃圾回收關係、基本類型常量池優化六個維度深度對比。
| 區域 | 線程私有/共享 | 主要內容 |
|---|---|---|
| 程序計數器 | 私有 | 當前執行字節碼行號 |
| 虛擬機棧 | 私有 | 棧幀:局部變量表、操作數棧、動態鏈接、方法出口 |
| 本地方法棧 | 私有 | Native 方法調用 |
| 堆 | 共享 | 對象實例、數組、字符串常量池(JDK7+)、類加載器緩存 |
| 方法區 | 共享 | 類元數據、常量、靜態變量、即時編譯代碼(Metaspace/JDK8+) |
| 維度 | 棧 | 堆 |
|---|---|---|
| 存儲內容 | 基本類型局部變量、對象引用、方法調用幀 | 對象實例、數組、字符串常量池 |
| 訪問速度 | 極快(指針移動),僅次於寄存器 | 較慢(需堆分配、GC 掃描) |
| 生命周期 | 隨線程/方法創建銷毀,確定性釋放 | 對象創建於 new,銷毀由 GC 不確定性回收 |
| 大小限制 | -Xss 設置(默認 1MB),棧溢出拋 StackOverflowError | -Xms/-Xmx 設置,堆耗盡拋 OutOfMemoryError |
| GC 管理 | 不受 GC 管理,自動釋放 | GC 核心管理區域(新生代、老年代) |
| 線程隔離 | 每線程一棧,天然隔離 | 全局共享,需同步/鎖/ThreadLocal 隔離 |
int a = 3; // 棧創建引用 a → 常量池查找 3 → 不存在則存入 → a 指向池中 3
int b = 3; // 棧創建引用 b → 常量池已有 3 → b 直接指向同一 3(節省空間)
a = 4; // 常量池存入 4 → a 指向 4,b 不受影響仍指向 3
適用範圍:Integer -128~127、Byte、Short -128~127、Character 0~127、Boolean、String(字面量)。new Integer(3) 繞過常量池直接在堆創建新對象。
public class MemoryDemo {
static int staticVar = 10; // 方法區(類變量)
public void method() {
int localVar = 20; // 棧(局部變量)
Object obj = new Object(); // 棧存引用 obj,堆存 Object 實例
int[] arr = new int[5]; // 棧存引用 arr,堆存 int[5] 數組對象
String s = "literal"; // 棧存引用 s,指向字符串常量池
String s2 = new String("literal"); // 棧存引用 s2,堆創建新 String 對象
}
}
new String() 在堆;intern() 可手動入池。StackOverflowError 或 OutOfMemoryError: unable to create new native thread。-XX:PretenureSizeThreshold)。-Xss(如 256k-512k),支持更多線程併發。ThreadLocal 實現線程隔離變量,本質是線程私有 Map 存儲在堆,但邏輯上屬於線程局部。