文章目錄
- 1 三色標記流程
- 1.1 初始標記
- 1.2 并發標記
- 1.3 重新標記
- 1.4 清除階段(Sweep)
- 1.5 為什么初始標記和重新標記需要STW,而并發標記不需要?
- 2 并發標記的寫屏障
- 3 多標問題
- 4.漏標問題
- 4.1 漏標的兩個必要條件
- 4.2 解決方案一:增量更新(CMS)
- 4.3 解決方案二:原始快照(SATB,G1)
- 4.4 為什么 G1 選擇 SATB?
- 5.面試回答模板
1 三色標記流程
三色標記算法是一種JVM中垃圾標記的算法,他可以減少JVM在GC過程中的STW時長,他是CMS、G1等垃圾收集器中主要使用的標記算法。
在出現三色標記算法之前,JVM中垃圾對象的標記主要采用可達性分析算法及引用計數法。但是這兩種算法存在以下問題:
- 1、循環引用問題,如果兩個對象互相引用,就形成了一個環形結構,如果采用引用計數法的話,那么這兩個對象將永遠無法被回收。
- 2、STW時間長,可達性分析的整個過程都需要STW,以避免對象的狀態發生改變,這就導致GC停頓時長很長大大影響應用的整體性能。
為了解決上面這些問題,就引入了三色標記法.
三色標記法將對象分為三種狀態:白色、灰色和黑色
- 白色:該對象沒有被標記過
- 灰色: 該對象已經被標記過了,但該對象的引用對象還沒標記完
- 黑色: 該對象已經被標記過了,并且他的全部引用對象也都標記完了,
三色標記法的標記過程可以分為三個階段: 初始標記(Initial Marking)、并發標記 (Concurrent Marking)和重新標記 (Remark)。
1.1 初始標記
- 目標:標記所有GC Roots 直接引用的對象。
- 操作:
- 所有對象初始為 白色。
- 從 GC Roots(如虛擬機棧引用、方法區靜態屬性、本地方法棧引用等)出發,將 直接引用的對象 標記為 灰色。
- 初始標記階段只掃描 GC Roots 的直接引用鏈,不深入遍歷整個對象圖。
- 是否 STW:是(Stop The World)。
- 特點:時間短,僅掃描根對象的直接引用。
1.2 并發標記
- 目標:從灰色對象出發,遍歷整個對象圖,標記所有可達對象。
- 操作:
- 從灰色集合中取出對象,將其標記為 黑色,并將其引用的白色對象標記為 灰色。
- 重復上述過程,直到灰色集合為空。
- 在此階段,應用程序線程與 GC 線程并發執行,用戶代碼可能修改對象引用關系。
- 是否 STW:否(無需 Stop The World)。
- 關鍵問題:
- 并發修改的挑戰:用戶線程可能新增或斷開引用,導致漏標或多標(如黑色對象新增引用白色對象)。
- 解決方案:使用 寫屏障(Write Barrier) 技術,攔截對象引用的修改操作,并更新標記狀態。
- 增量更新(Incremental Update):當黑色對象新增指向白色對象的引用時,記錄該引用,后續重新掃描。
- 刪除寫屏障(Destructive Write Barrier):當白色對象的引用被斷開時,將其標記為灰色。
- 耗時分析:
- 最耗時的階段,需遍歷整個對象圖。
- 優勢:通過并發執行,減少 STW 時間,提升系統響應性。
1.3 重新標記
- 目標:修正并發標記階段中因用戶線程修改對象引用導致的漏標或誤標。
- 操作:
- 從灰色集合重新開始遍歷對象圖,修正標記狀態。
- 處理 未被并發標記階段遍歷到的對象(如新增的引用)。
- 是否 STW:是(Stop The World)。
- 特點:
- 時間通常較短,因為只需修正少量錯誤
以上三個標記階段中,初始標記和重新標記是需要STW的,而并發標記是不需要STW的。其中最耗時的其實就是并發標記的這個階段,因為這個階段需要遍歷整個對象樹,而三色標記把這個階段做到了和應用線程并發執行,大大降低了GC的停頓時長
1.4 清除階段(Sweep)
- 目標:回收所有 白色對象(不可達對象)。
- 操作:
- 遍歷堆內存,回收白色對象的內存空間。
- 將黑色對象重置為白色,以便下次 GC 使用。
- 是否 STW:否(部分垃圾收集器可并發執行)。
1.5 為什么初始標記和重新標記需要STW,而并發標記不需要?
在初始標記階段,針對根 (GCRoot)直接引用的對象進行標記,這個過程也通常被叫做根掃描。
為了防止在初始標記過程中根對象被修改,這個過程是STW的,雖然G1可以通過采用寫屏障技術來獲知對象是否發生了修改,但是因為大多數的GCRoot他并不是對象,所以無法被獲知的,所以,這個階段是需要進行STW的。
重新標記階段,目的是修正并發標記階段因應用程序繼續運行而產生的任何變化(因為并發標記沒有STW,所以會有變化)。此時,需要重新檢查和更新那些在并發標記階段可能發生變化的對象標記信息。
重新標記是清理前的最后一次標記,需要確保這個過程的準確性,所以需要做STW來保證。
總之,三個階段,為了提升性能肯定是能不STW就不STW,而最后一個階段一一重新標記因為是最終階段,所以需要STW來確保準確性。而第一個階段一初始標記,因為無法感知到GCRoot的變化,所以需要做STW來確保這個階段的準確性。
2 并發標記的寫屏障
并發標記過程中,應用程序線程可能會修改對象圖,因此垃圾回收器需要使用寫屏障 (Write Barrier) 技術來保證并發標記的正確性
寫屏障是一種在對象引用被修改時,將其新的引用信息記錄在特殊數據結構中的機制。在三色標記法中,寫屏障技術被用于記錄對象的標記狀態,并且只對未被標記過的對象進行標記。
當應用程序線程修改了一個對象的引用時,寫屏障會記錄該對象的新標記狀態。如果該對象未被標記過,那么它會被標記為灰色,以便在垃圾回收器的下一次遍歷中進行標記。如果該對象已經被標記為可達對象,那么寫屏障不會對該對象進行任何操作。
通過使用寫屏障技術,可以使得三色標記法過程中標記更加準確。然而,盡管寫屏障對于維護垃圾收集器的準確性至關重要,它們仍然存在一些局限性,
- 1.性能開銷.: 寫屏障會引入額外的性能開銷,因為每次對象引用更新時都需要執行額外的代碼。這種開銷可能導致系統性能下降,尤其是在高度并發的場景中
- 2.并發修改的挑戰: 在高度并發的應用中,對象的引用可能會頻繁變化。寫屏障需要在每次引用變化時及時更新信息,但在極端并發條件下,可能難以捕捉到所有的變化。
- 3.保守策略導致的多標: 為了避免誤刪除有效對象,一些垃圾收集器可能采取保守策略,在存在不確定性時選擇保留對象。這可能導致實際上已經不再使用的對象被錯誤地標記為存活。
- 4.優化策略的雙刃劍: 為了減輕性能開銷,某些垃圾收集器可能采用優化策略,例如只在特定條件下激活寫屏障。這種優化有可能導致某些引用更新被錯過,影響標記的準確性。
3 多標問題
所謂多標,其實就是這個對象原本應該被回收掉的白色對象,但是被錯誤的標記成了黑色的存活對象。從而導致這個對象沒有被GC回收掉。
這個一般發生在并發標記過程中,該對象還是有引用的,但是在過程中,應用程序執行過程中把他的引用關系刪除了,導致他變成了一個垃圾對象,
多標的話,會產生浮動垃圾,這個問題一般都不太需要解決,因為這種垃圾一般都不會太多,另外在下一次GC的時候也都能被回收掉。
4.漏標問題
4.1 漏標的兩個必要條件
- 黑色對象新增引用白色對象(條件①)
- 黑色對象(已標記為存活)新增指向白色對象(未被標記)的引用,導致白色對象被漏標。
- 灰色對象刪除對白色對象的引用(條件②)
- 灰色對象(正在掃描)在掃描完成前斷開對白色對象的引用,導致白色對象失去所有路徑連接。
漏標必須同時滿足這兩個條件才會發生。因此,解決方案只需要 破壞其中一個條件 即可。
4.2 解決方案一:增量更新(CMS)
目標:破壞條件①(黑色對象新增引用白色對象)
- 核心思想:如果黑色對象新增了對白色對象的引用,就 將黑色對象重新標記為灰色,并在后續重新掃描其引用鏈。這樣白色對象會被標記為灰色,避免漏標。
- 具體操作:
- 寫屏障:當黑色對象新增引用白色對象時,記錄該引用關系。
- 重新標記階段:以這些新增的引用為起點,重新掃描黑色對象的引用鏈。
- 類比:
- 假設你在打掃房間(GC),已經標記某個抽屜(黑色對象)里的物品為“必須保留”。此時家人往抽屜里放了新東西(白色對象)。
- 增量更新就像家人給你留個便條:“這個抽屜有新增物品,請重新檢查”,你就會回去重新掃描抽屜,確保新物品不被遺漏。
- 優點:
- 不產生浮動垃圾(所有被引用的對象都會被正確標記)。
- 缺點:
- 需要重新掃描整個引用鏈,耗時較長(尤其是引用鏈復雜時)
4.3 解決方案二:原始快照(SATB,G1)
目標:破壞條件②(灰色對象刪除對白色對象的引用)
- 核心思想:在 GC 開始時,記錄所有存活對象的狀態(快照)。即使后續引用被刪除,也以快照為準,確保白色對象不會被漏標。
- 具體操作:
- 寫屏障:當灰色對象刪除對白色對象的引用時,記錄該白色對象為“快照中的存活對象”。
- 重新標記階段:以這些白色對象為起點,重新掃描它們的引用鏈。
4.4 為什么 G1 選擇 SATB?
- 性能優先:
- SATB 只需掃描被刪除引用的對象(通過 RSet 和 Card Table 快速定位),而增量更新需要重新掃描整個引用鏈。
- 對于大堆場景(如 G1 的 Region 劃分),SATB 的效率優勢更明顯。
- 浮動垃圾容忍度高:
- 浮動垃圾只是延遲到下一輪 GC 清理,不會影響當前 GC 的準確性。相比而言,增量更新的耗時可能影響系統響應時間。
- 與 Region 結構適配:
- G1 的 Region 結構天然支持 RSet(記錄跨 Region 引用),方便快速定位被刪除引用的對象。
5.面試回答模板
垃圾回收機制需要分為兩個步驟進行,第一標記出哪些是需要回收的垃圾,第二根據相應的清除機制如:標記復制、標記清除、標記整理等,而三色標記就是為了避免標記垃圾時STW的問題,三色標記將對象分為三種顏色,白色、灰色、黑色、其中白色表示未被引用的對象也是需要刪除的對象,灰色表示對象已經被標記,但其引用還未標記,黑色表示對象及其引用都已經被標記,整個標記過程主要分為三個階段:初始標記、并發標記和重新標記,初始標記主要是從GC Root出發,直接將引用對象標記為灰色,此時會引起stw,但耗時較短,并發標記從灰色對象出發,遍歷整個對象圖,耗時最長,但是不會引起stw,會和工作線程并發執行,但是并發執行期間會產生一些浮動的垃圾,這些浮動垃圾需要依靠重新標記去修正,此時也會引起stw,防止產生更多的浮動垃圾,并發標記階段會通過寫屏障來保證并發標記的準確性,針對漏標的問題一般可以通過增量更新或快照的方式去解決