今天我們一起來聊一聊 JVM 堆內存。
Java Heap(堆內存)由 Young Generation(新生代,約佔 1/3 )和 Old Generation(老年代,約佔 2/3 )組成。
Young Generation 又由 Eden Space(伊甸園區,佔新生代 80% )、Survivor Space 0(倖存者區0,佔新生代 10% )和 Survivor Space 1(倖存者區1,佔新生代 10% )組成。
對象的生命週期:
Young Generation(新生代):
- 存儲 新創建、存活週期極短 的對象,比如方法內的局部變量、臨時對象。默認佔堆總容量 1/3 。
- 觸發 Minor GC(新生代 GC),頻率極高(毫秒 / 秒級),但耗時極短(幾十毫秒)。
- Eden 區是 Minor GC 的 "觸發源頭",調大 Eden 區可減少 Minor GC 次數。
Eden Space(伊甸園區):新對象的 "默認出生地",99% 的新對象 都會優先分配到 Eden 區(除非是超大對象直接進入老年代)。佔新生代 80% 。
Survivor Space 0/1(倖存者區 0/1,簡稱 S0/S1,也叫 From 區 / To 區):
- 新生代 GC 後存活對象的 "臨時中轉站",避免存活對象直接進入老年代。兩個區會動態互換角色。各佔新生代 10%,合計 20%。
- S0 和 S1 永遠有一個是空的(這是 JVM 的設計巧思)。
- 每經歷一次 Minor GC,存活對象的 "年齡 + 1"。
S0/S1 的交互 示例:
- ① 初始狀態:Eden 有對象,S0 有對象,S1 為空;
- ② Eden 觸發 Minor GC → 回收 Eden+S0 的垃圾對象,存活對象複製到 S1,清空 Eden+S0;
- ③ 此時 S1 非空、S0 為空,兩者角色互換(S0=To 區,S1=From 區);
- ④ 下次 Minor GC 重複上述過程,存活對象在 S0/S1 之間 "來回複製"。
Old Generation(老年代):
- 存儲 長期存活、體積較大 的對象,比如單例對象、緩存對象、經歷多次 Minor GC 仍存活的對象。默認佔堆總容量 2/3。
- 觸發 Major GC / Full GC,頻率極低,但耗時極長。
- 老年代大小決定 Full GC 頻率 —— 老年代越大,Full GC 頻率越低,但單次 Full GC 耗時越長。
對象進入老年代的條件:
- 年齡達標:對象在 S0/S1 之間複製次數達到閾值(默認 15 次,
-XX:MaxTenuringThreshold調整); - 大對象:超過閾值的對象(
-XX:PretenureSizeThreshold)直接進入老年代; - 空間擔保失敗:Minor GC 後 Survivor 區放不下存活對象,多餘對象直接晉升老年代;
- 動態年齡判斷:Survivor 區中相同年齡對象總和超過 50%,該年齡及以上對象直接進入老年代。
不同存活週期的對象放在不同區域,用不同算法回收,保證 GC 效率,這就是 "分代回收" 的核心思想。
努力的意義,不在於追逐別人的高度,而是拼盡全力,為自己創造一個絕地反擊的故事。-- 煙沙九洲