STW是什么?——深入理解JVM垃圾回收中的"Stop-The-World"
在Java程序運行過程中,JVM會通過垃圾回收(GC)自動管理內存,釋放不再使用的對象以騰出空間。但你是否遇到過程序突然卡頓的情況?這可能與GC過程中的??Stop-The-World(STW,全局停頓)??有關。本文將圍繞"GC何時STW"展開,重點解析CMS與G1回收器的STW機制,并結合三色標記法說明其必要性。
一、STW的本質與核心作用
??Stop-The-World(STW)?? 是JVM在垃圾回收過程中,為保證內存回收的??正確性??和??一致性??,臨時暫停所有應用線程(User Thread)執行的現象。簡單來說,就是"世界停止了",只有GC線程在工作。
為什么需要STW?
垃圾回收的核心是??準確區分存活對象與可回收對象??。若應用線程在GC過程中繼續運行,可能導致:
- ??對象狀態變更??:應用線程可能修改對象的引用關系(如創建新對象、銷毀舊對象、修改指針指向),導致GC標記結果失效;
- ??數據不一致??:若GC線程與應用線程同時操作同一塊內存,可能引發競態條件(Race Condition),破壞內存管理的準確性。
因此,STW是GC保證自身邏輯正確的"保護機制",但過長或頻繁的STW會顯著降低程序性能(尤其是對延遲敏感的應用)。
二、三色標記法:GC標記對象的"通用語言"
無論是CMS還是G1,GC標記階段均基于??三色標記法??(Tri-Color Marking)實現。這是一種通過顏色標記對象存活狀態的并發標記算法,三種顏色含義如下:
顏色 | 含義 |
---|---|
白色 | 未被GC線程訪問過的對象,默認視為"可回收垃圾"(未被標記)。 |
灰色 | 已被GC線程訪問過,但其??引用的其他對象??尚未處理(待遍歷的"邊界"對象)。 |
黑色 | 已被GC線程完整處理(自身標記為存活,且所有引用對象也已處理),確認"存活"。 |
標記過程的關鍵:
GC線程從GC Roots(如棧幀局部變量、靜態變量、JNI引用等)出發,將直接關聯的對象標記為灰色(初始階段);隨后遞歸處理灰色對象的引用,將其目標對象標記為灰色,自身升級為黑色(并發階段)。最終未被標記為黑色的白色對象將被回收。
但三色標記法存在一個天然缺陷:??若應用線程在標記過程中修改了對象的引用關系(如刪除灰色對象到白色對象的引用),可能導致白色對象被錯誤回收(漏標)??。因此,GC需要通過STW階段修正這些變動。
三、CMS回收器的STW階段解析
CMS(Concurrent Mark-Sweep,并發標記-清除)是早期的并發GC算法,目標是??減少STW時間??,適用于對延遲敏感的場景。其核心流程包含4個階段,其中??初始標記??和??重新標記??需要STW,其余階段與應用線程并發執行。
1. 初始標記(Initial Mark,STW)
- ??目標??:快速標記GC Roots直接關聯的對象(即從GC Roots出發的第一層可達對象)。
- ??STW原因??:需暫停所有應用線程,確保標記的準確性(避免應用線程在此時修改GC Roots的引用關系)。
- ??耗時??:非常短暫(通常僅毫秒級),因為僅標記直接關聯對象。
2. 并發標記(Concurrent Mark,并發)
- ??目標??:從初始標記的灰色對象出發,遞歸遍歷所有可達對象,將其標記為黑色(存活)。
- ??STW狀態??:與應用線程并發執行(不暫停)。
- ??風險??:若應用線程在并發標記期間修改了對象的引用關系(如刪除灰色對象到白色對象的引用),可能導致部分存活對象被漏標為白色。
3. 重新標記(Remark,STW)
- ??目標??:修正并發標記階段因應用線程運行導致的標記變動(如漏標、錯標)。
- ??STW原因??:需暫停應用線程,確保所有標記變動被正確處理(例如,通過"增量更新"算法記錄并發期間的引用變更,重新掃描這些變更的對象)。
- ??耗時??:比初始標記長,但遠短于完全串行的標記過程(通常為初始標記的數倍)。
4. 并發清除(Concurrent Sweep,并發)
- ??目標??:回收未被標記的白色對象(垃圾),釋放內存空間。
- ??STW狀態??:與應用線程并發執行(不暫停)。
- ??特點??:CMS采用"標記-清除"算法,因此不會移動存活對象,可能導致內存碎片(長期運行后可能引發Full GC)。
CMS的STW總結:
CMS通過并發標記和清除大幅減少了STW時間,但初始標記和重新標記仍需短暫停頓。其STW總耗時通常在10ms~100ms級別(取決于堆大小和對象復雜度)。
四、G1回收器的STW階段解析
G1(Garbage-First)是JDK9及以后默認的GC算法,設計目標是??平衡吞吐量與延遲??(支持設置最大停頓時間)。與CMS不同,G1將堆劃分為多個獨立的Region(默認2MB~32MB),并通過"標記-整理"思想優化內存布局。其核心流程同樣包含4個階段,但STW的觸發邏輯與CMS有顯著差異。
1. 初始標記(Initial Mark,STW)
- ??目標??:標記GC Roots直接關聯的對象,并記錄每個Region中"已存活對象"的數量(用于后續回收價值排序)。
- ??STW狀態??:與應用線程并發執行(僅標記GC Roots直接關聯的對象,耗時極短)。
- ??特點??:G1的初始標記通常與Minor GC(年輕代GC)合并執行,進一步減少STW時間。
2. 并發標記(Concurrent Mark,并發)
- ??目標??:遞歸標記所有可達對象,統計每個Region的存活對象比例。
- ??STW狀態??:與應用線程并發執行(不暫停)。
- ??優化??:G1通過"SATB(Snapshot-At-The-Beginning)"算法記錄初始標記時的對象快照,即使后續應用線程修改引用關系,也能通過對比快照修正標記(減少漏標)。
3. 最終標記(Final Mark,STW)
- ??目標??:處理并發標記階段SATB快照中未被處理的變動(如新增的白色對象),確保標記結果最終準確。
- ??STW狀態??:與應用線程短暫并發(僅掃描SATB隊列中的變更,耗時通常在1ms~5ms)。
4. 篩選回收(Live Data Counting and Evacuation,STW)
- ??目標??:根據各Region的存活對象比例和回收價值(存活對象越少、Region越小,回收價值越高),選擇部分Region進行回收,并將存活對象移動到其他Region(整理內存)。
- ??STW原因??:需暫停應用線程,確保存活對象移動過程的原子性(避免應用線程訪問正在移動的對象)。
- ??耗時??:取決于需要回收的Region數量(若僅回收少量Region,STW可控制在10ms以內)。
G1的STW總結:
G1的STW主要集中在初始標記、最終標記和篩選回收階段,但通過并發標記和SATB算法大幅縮短了單次STW的時間。由于G1支持"增量回收"(每次只回收部分Region),其平均STW時間可控制在用戶設定的閾值內(如不超過200ms)。
五、CMS與G1的STW對比
維度 | CMS | G1 |
---|---|---|
??STW階段?? | 初始標記、重新標記 | 初始標記、最終標記、篩選回收 |
??單次STW時長?? | 較長(重新標記可能達百毫秒級) | 更短(篩選回收可通過Region選擇優化) |
??內存碎片?? | 標記-清除算法導致碎片積累 | 標記-整理算法避免碎片 |
??適用場景?? | 老年代小堆(<8GB),對延遲敏感 | 大堆(>8GB),需平衡吞吐量與延遲 |
總結
STW是GC保證正確性的必要代價,但通過優化標記算法(如三色標記+SATB)和并發設計(如CMS的并發標記、G1的Region劃分),現代GC已將STW時間控制在可接受范圍內。理解不同回收器的STW階段,有助于我們在實際開發中根據業務需求(如延遲敏感型或吞吐量優先型)選擇合適的GC算法,并通過JVM參數調優(如-XX:+UseConcMarkSweepGC
或-XX:+UseG1GC
)進一步降低停頓時間。