首頁 > 科技

回懟面試官!一文帶你深入理解JVM垃圾回收,學完去面試再也不慌

2021-06-25 16:58:14

Hello,今天給各位童鞋們分享的是JVM垃圾回收,趕緊拿出小本子記下來吧

1. Jvm垃圾回收

Java虛擬機器主要分為五大模組:類裝載器子系統、運行時資料區、執行引擎、本地方法介面和垃圾收集模組。其中垃圾收集模組在Java虛擬機器規範中並沒有要求Java虛擬機器垃圾收集,但是在沒有發明無限的記憶體之前,大多數JVM實現都是有垃圾收集的。

Java堆是記憶體管理中最大的一塊,所有的執行緒共享這一塊內容,同時該部分也是垃圾收集器的主要區域。

虛擬機器的垃圾回收機制是完善的,動態記憶體分配和回收是比較成熟的,在記憶體管理機制中,大部分都不需要我們考慮記憶體回收,只有Java堆和方法區需要我們考慮處理記憶體問題。一般的對於記憶體回收首先就是判斷某一個部分是生存還是死亡,主要是通過下面二種演算法:

其一是引用計數演算法,本演算法實現簡單,判定的效率也是比較高的,很多的軟體都使用了該演算法,但是主流的Java並沒有選擇該演算法,核心的問題是該演算法難以處理物件之間相互呼叫的問題。

其二是稱可達性分析演算法,該演算法核心思想是依靠判斷物件是否存活來實現的,本演算法是通過一系列的GC ROOTS的物件作為起始點,採用搜尋的演算法遍歷引用鏈,如果搜尋過程中沒有發現該節點,則認為該節點是不可達的,即可回收的,在Java裡面,一般可以使用該演算法處理問題。

2. 作用域

JVM 堆年輕代老年代元空間3. 分類

當前虛擬機器的垃圾收集都採用分代收集演算法,這種演算法沒有什麼新的思想,只是根據物件存活週期的不同將記憶體分為幾塊。一般將java堆分為新生代和老年代,這樣我們就可以根據各個年代的特點選擇合適的垃圾收集演算法。

4. 垃圾回收演算法

4.1 標記-複製演算法

為了解決效率問題,「複製」收集演算法出現了。它可以將記憶體分為大小相同的兩塊,每次使用其中的一塊。當這一塊的記憶體使用完後,就將還存活的物件複製到另一塊去,然後再把使用的空間一次清理掉。這樣就使每次的記憶體回收都是對記憶體區間的一半進行回收

4.2 標記-清除演算法

演算法分為「標記」和「清除」階段:標記存活的物件, 統一回收所有未被標記的物件(一般選擇這種);也可以反過來,標記出所有需要回收的物件,在標記完成後統一回收所有被標記的物件 。它是最基礎的收集演算法,比較簡單,但是會帶來兩個明顯的問題:

效率問題 (如果需要標記的物件太多,效率不高)空間問題(標記清除後會產生大量不連續的碎片)

4.3 標記-整理演算法

根據老年代的特點特出的一種標記演算法,標記過程仍然與「標記-清除」演算法一樣,但後續步驟不是直接對可回收物件回收,而是讓所有存活的物件向一端移動,然後直接清理掉端邊界以外的記憶體。

5. 垃圾收集器

5.1 Serial收集器(-XX:+UseSerialGC -XX:+UseSerialOldGC)

Serial(序列)收集器是最基本、歷史最悠久的垃圾收集器了。大家看名字就知道這個收集器是一個單執行緒收集器了。它的 「單執行緒」 的意義不僅僅意味著它只會使用一條垃圾收集執行緒去完成垃圾收集工作,更重要的是它在進行垃圾收集工作的時候必須暫停其他所有的工作執行緒( 「Stop The World」 ),直到它收集結束。

新生代採用複製演算法,老年代採用標記-整理演算法。

5.2 Parallel Scavenge收集器(-XX:+UseParallelGC(年輕代),-XX:+UseParallelOldGC(老年代))

Parallel收集器其實就是Serial收集器的多執行緒版本,除了使用多執行緒進行垃圾收集外,其餘行為(控制參數、收集演算法、回收策略等等)和Serial收集器類似。預設的收集執行緒數跟cpu核數相同,當然也可以用參數(-XX:ParallelGCThreads)指定收集執行緒數,但是一般不推薦修改。

Parallel Scavenge收集器關注點是吞吐量(高效率的利用CPU)。CMS等垃圾收集器的關注點更多的是使用者執行緒的停頓時間(提高使用者體驗)。所謂吞吐量就是CPU中用於運行使用者程式碼的時間與CPU總消耗時間的比值。 Parallel Scavenge收集器提供了很多參數供使用者找到最合適的停頓時間或最大吞吐量,如果對於收集器運作不太瞭解的話,可以選擇把記憶體管理優化交給虛擬機器去完成也是一個不錯的選擇。新生代採用複製演算法,老年代採用標記-整理演算法。

Parallel Old收集器是Parallel Scavenge收集器的老年代版本。使用多執行緒和「標記-整理」演算法。在注重吞吐量以及CPU資源的場合,都可以優先考慮 Parallel Scavenge收集器和Parallel Old收集器(JDK8預設的新生代和老年代收集器)。

5.3 ParNew收集器(-XX:+UseParNewGC)

ParNew收集器其實跟Parallel收集器很類似,區別主要在於它可以和CMS收集器配合使用。

新生代採用複製演算法,老年代採用標記-整理演算法。

它是許多運行在Server模式下的虛擬機器的首要選擇,除了Serial收集器外,只有它能與CMS收集器(真正意義上的併發收集器,後面會介紹到)配合工作。

5.4 CMS收集器(-XX:+UseConcMarkSweepGC(old))

CMS(Concurrent Mark Sweep)收集器是一種以獲取最短回收停頓時間為目標的收集器。它非常符合在注重使用者體驗的應用上使用,它是HotSpot虛擬機器第一款真正意義上的併發收集器,它第一次實現了讓垃圾收集執行緒與使用者執行緒(基本上)同時工作。

從名字中的Mark Sweep這兩個詞可以看出,CMS收集器是一種 「標記-清除」演算法實現的,它的運作過程相比於前面幾種垃圾收集器來說更加複雜一些。整個過程分為四個步驟:

初始標記: 暫停所有的其他執行緒(STW),並記錄下gc roots直接能引用的物件,速度很快。併發標記: 併發標記階段就是從GC Roots的直接關聯物件開始遍歷整個物件圖的過程, 這個過程耗時較長但是不需要停頓使用者執行緒, 可以與垃圾收集執行緒一起併發運行。因為使用者程式繼續運行,可能會有導致已經標記過的物件狀態發生改變。重新標記: 重新標記階段就是為了修正併發標記期間因為使用者程式繼續運行而導致標記產生變動的那一部分物件的標記記錄,這個階段的停頓時間一般會比初始標記階段的時間稍長,遠遠比並發標記階段時間短。主要用到三色標記裡的增量更新演算法(見下面詳解)做重新標記。併發清理: 開啟使用者執行緒,同時GC執行緒開始對未標記的區域做清掃。這個階段如果有新增物件會被標記為黑色不做任何處理(見下面三色標記演算法詳解)。併發重置:重置本次GC過程中的標記資料。

從它的名字就可以看出它是一款優秀的垃圾收集器,主要優點:併發收集、低停頓。但是它有下面幾個明顯的缺點:

對CPU資源敏感(會和服務搶資源);無法處理浮動垃圾(在併發標記和併發清理階段又產生垃圾,這種浮動垃圾只能等到下一次gc再清理了);它使用的回收演算法-「標記-清除」演算法會導致收集結束時會有大量空間碎片產生,當然通過參數-XX:+UseCMSCompactAtFullCollection可以讓jvm在執行完標記清除後再做整理執行過程中的不確定性,會存在上一次垃圾回收還沒執行完,然後垃圾回收又被觸發的情況,特別是在併發標記和併發清理階段會出現,一邊回收,系統一邊運行,也許沒回收完就再次觸發full gc,也就是"concurrent mode failure",此時會進入stop the world,用serial old垃圾收集器來回收

5.5 CMS的相關核心參數

-XX:+UseConcMarkSweepGC:啟用cms

-XX:ConcGCThreads:併發的GC執行緒數

-XX:+UseCMSCompactAtFullCollection:FullGC之後做壓縮整理(減少碎片)

-XX:CMSFullGCsBeforeCompaction:多少次FullGC之後壓縮一次,預設是0,代表每次FullGC後都會壓縮一次

-XX:CMSInitiatingOccupancyFraction: 當老年代使用達到該比例時會觸發FullGC(預設是92,這是百分比)

-XX:+UseCMSInitiatingOccupancyOnly:只使用設定的回收閾值(-XX:CMSInitiatingOccupancyFraction設定的值),如果不指定,JVM僅在第一次使用設定值,後續則會自動調整

-XX:+CMSScavengeBeforeRemark:在CMS GC前啟動一次minor gc,降低CMS GC標記階段(也會對年輕代一起做標記,如果在minor gc就幹掉了很多對垃圾物件,標記階段就會減少一些標記時間)時的開銷,一般CMS的GC耗時 80%都在標記階段

-XX:+CMSParallellnitialMarkEnabled:表示在初始標記的時候多執行緒執行,縮短STW

-XX:+CMSParallelRemarkEnabled:在重新標記的時候多執行緒執行,縮短STW;

6. 垃圾收集底層演算法實現

三色標記在併發標記的過程中,因為標記期間應用執行緒還在繼續跑,物件間的引用可能發生變化,多標和漏標的情況就有可能發生。

這裡我們引入「三色標記」來給大家解釋下,把Gcroots可達性分析遍歷物件過程中遇到的物件, 按照「是否訪問過」這個條件標記成以下三種顏色:

黑色: 表示物件已經被垃圾收集器訪問過, 且這個物件的所有引用都已經掃描過。 黑色的物件代表已經掃描過, 它是安全存活的, 如果有其他物件引用指向了黑色物件, 無須重新掃描一遍。 黑色物件不可能直接(不經過灰色物件) 指向某個白色物件。灰色: 表示物件已經被垃圾收集器訪問過, 但這個物件上至少存在一個引用還沒有被掃描過。白色: 表示物件尚未被垃圾收集器訪問過。 顯然在可達性分析剛剛開始的階段, 所有的物件都是白色的, 若在分析結束的階段, 仍然是白色的物件, 即代表不可達。7 小結

Jvm優化主要是防止fullgc,縮短STW時間,杜絕OOM出現,而實現這一手段主要是依賴垃圾回收機制,具體來說就是垃圾回收器,而垃圾回收器又分了好多種,單執行緒,併發,回收演算法的差異性等等,所以要做到深度優化,必須理解其底層機制。

好啦,今天的文章就到這裡了,希望能夠幫助到螢幕前迷茫的你們


IT145.com E-mail:sddin#qq.com