面試官:怎么做JDK8的垃圾收集器的調優?


面試官:怎么做JDK8的垃圾收集器的調優?

面試官

看着面試官真誠的眼神,心中暗想看起來年紀輕輕卻提出如此直擊靈魂的問題。擦了擦額頭上汗,我稍微調整了一下緊張的情緒,對面試官說:

在JDK8中有Serial收集器、Parallel收集器、CMS收集器、G1收集器這么幾種收集器,需要根據實際硬件配置和業務需求進行選擇調優。

如此淺顯的回答,無法讓面試官達到深入的要求,肯定不能滿足面試官強烈的需求,果不其然面試官又追問到:如果是桌面應用,內存占用也就100MB,應該選擇哪種垃圾收集器呢?我快速的回答:Serial收集器。看着面試官期待的眼神,我又詳細解釋到:

文章持續更新,微信搜索「萬貓學社第一時間閱讀,關注后回復「電子書」,免費獲取12本Java必讀技術書籍。

Serial收集器

Serial收集器是使用單線程處理所有的垃圾收集工作的,因為沒有多線程的額外開銷,相對來說也是比較有效的。所以,最適合單核CPU環境,因為本來也沒辦法利用多核。不過,當應用的使用的內存大小在100MB左右甚至更小的時候,在也適用於多核CPU的環境。

我一邊說着,一邊在紙上畫了起來:

Client模式的JVM默認的垃圾收集器就是Serial收集器,或者可以使用JVM參數-XX:+UseSerialGC顯式啟用Serial收集器。

面試官又追問到:如果是要求高吞吐量的應用,使用較大內存並且有多核CPU,應該選擇哪種垃圾收集器呢?我快速的回答:Parallel收集器。看着面試官期待的眼神,我又詳細解釋到:

文章持續更新,微信搜索「萬貓學社第一時間閱讀,關注后回復「電子書」,免費獲取12本Java必讀技術書籍。

Parallel收集器

Parallel收集器是類似於Serial收集器的分代收集器,主要區別是在垃圾回收的時候使用了多個線程進行加速垃圾的收集。所以,對於使用較大內存並且有多核CPU的環境更加適合。

我一邊說着,一邊在紙上畫了起來:

Server模式的JVM默認的垃圾收集器就是Parallel收集器,也可以使用JVM參數-XX:+UseParallelGC啟用。啟用Parallel收集器后默認情況下,Minor垃圾收集(針對年輕代的垃圾收集)和Major垃圾收集(針對老年代的垃圾收集)都是並行執行的,可以進一步減少垃圾收集的開銷。

Parallel收集器可以通過JVM參數指定最大垃圾收集暫停時間、吞吐量(用戶代碼運行時間/(用戶代碼運行時間+垃圾收集運行時間))和堆占用空間的目標值:

  • -XX:MaxGCPauseMillis:最大垃圾收集暫停時間,單位為毫秒,如:-XX:MaxGCPauseMillis=200,表示垃圾收集暫停時間最大為200毫秒。默認情況下,沒有指定最大垃圾收集暫停時間。如果指定了暫停時間目標,則會調整堆大小與垃圾收集相關的其他參數,使垃圾收集的暫停時間短於指定值。這些調整可能導致降低應用的整體吞吐量,也有可能無法始終滿足所指定的最大垃圾收集暫停時間目標。
  • -XX:GCTimeRatio:吞吐量大小,如:-XX:GCTimeRatio=19,表示將垃圾收集運行時間的目標設定為應用總運行時間(用戶代碼運行時間+垃圾收集運行時間)的1/(1+19),即5%。默認值為99,垃圾收集的目標時間占應用總運行時間的1/(1+99),即1%。
  • -Xmx:堆占用的最大占用空間,如:-Xmx1G,表示堆占用的最大占用空間為1GB。另外,Parallel收集器還有一個隱含的目標:只要滿足其他目標的同時,把堆占用內存的大小最小化。

這三個目標是有優先級的:

  1. 高優先級:最大垃圾收集暫停時間
  2. 中優先級:吞吐量目標
  3. 低優先級:最小堆占用內存目標

Parallel收集器按照指定的目標對分代大小和底層進行自動調節,盡量達到指定的目標,但不保證百分之百能達到。

面試官又追問到:如果同樣是使用較大內存並且有多核CPU,但是要求垃圾收集暫停時間要盡可能短的Web應用,應該選擇哪種垃圾收集器呢?我稍微思考了一下,回答:CMS收集器。看着面試官期待的眼神,我又詳細解釋到:

文章持續更新,微信搜索「萬貓學社第一時間閱讀,關注后回復「電子書」,免費獲取12本Java必讀技術書籍。

CMS收集器

CMS(Concurrent Mark Sweep)收集器是為那些要求垃圾收集暫停時間盡可能短,並且可以和垃圾收集器共享CPU資源的應用設計的。具有相對較大的內存使用並有多核CPU的應用,往往會更適合CMS收集器的使用。可以使用JVM參數-XX:+UseConcMarkSweepGC啟用CMS收集器,啟用后同時作用於Minor垃圾收集(針對年輕代的垃圾收集)和Major垃圾收集(針對老年代的垃圾收集)。

CMS收集器嘗試通過使用單獨的垃圾收集器線程在執行用戶線程的同時並跟蹤可訪問對象,來減少由於Major垃圾收集而導致的暫停時間。在每個Major垃圾收集周期中,CMS收集器會在收集開始時暫停所有用戶線程一小段時間,然后在收集的中期再次暫停。第二個暫停往往是兩個暫停中較長的一個,在兩個暫停之間都使用多個線程並行做收集工作的。所以,CMS收集器的垃圾收集過程分為以下四個步驟:

  1. 初始標記(CMS initial mark):這個步驟會暫停所有用戶線程,但耗時非常短,標記GC Root直接關聯的對象。
  2. 並發標記(CMS concurrent mark):這個步驟耗時較長,但用戶線程可同時運行,標記至GC Root有可達路徑的對象。
  3. 重新標記(CMS remark):這個步驟會暫停所有用戶線程,但耗時比較短。由於步驟2用戶線程同步運行,所以要修正在步驟二中用戶線程同步運行產生對象標記的變動。
  4. 並發清除(CMS concurrent sweep):這個步驟耗時較長,但用戶線程可同時運行。

我一邊說着,一邊在紙上畫了起來:

面試官繼續追問到:如果堆中有超過50%的活躍對象,分配對象和對象升代的頻率較高,垃圾收集停頓時間大於0.5秒,應該選擇哪種垃圾收集器呢?我稍微思考了一下,回答:G1收集器。看着面試官期待的眼神,我又詳細解釋到:

文章持續更新,微信搜索「萬貓學社第一時間閱讀,關注后回復「電子書」,免費獲取12本Java必讀技術書籍。

G1收集器

G1(Garbage-First)收集器是一款主要面向服務端應用的垃圾收集器,適用於具有大內存的多核CPU的服務器。它嘗試在高概率下同時滿足較小的垃圾收集暫停時間和較高的吞吐量。所有堆相關的操作(如:全局標記)與用戶線程同時運行,這樣可以避免隨着堆內存的大小的增加垃圾收集的停頓時間也跟着增加。

G1收集器是垃圾收集技術歷史上里程碑的成果,它跳出了之前收集整個代垃圾的思維模式,開創了收集器面向局部收集的設計思路和基於Rigion的內存布局形式。在之后的JDK版本中,G1收集器正在逐漸成為了CMS收集器的替代者和繼任者。

G1收集器雖然遵循分代收集的設計,但是整個堆的內存設計有顯著的不同。整個堆被划分為一組大小相等的獨立區域(Region),每個獨立區域(Region)都有一個連續的虛擬內存范圍,並且根據需要在邏輯被划分為年輕代的Eden區、Survivor區或者老年代。

我一邊說着,一邊在紙上畫了起來:

通過JVM參數-XX:MaxGCPauseMillis來給G1收集器指定垃圾收集的目標停頓時間,默認是200毫秒。G1收集器會使用預測模型來估算停頓時間內可以收集多少個獨立區域。在一次垃圾回收結束時,G1收集器會選擇下次將要收集哪些獨立區域。通常情況下,G1收集器通過選擇年輕代獨立區域的數量來控制垃圾收集的停頓時間。與其他垃圾收集器一樣,可以通過參數指定年輕代的大小,但是這樣做可能會影響G1收集器達到停頓時間目標的效果。除了停頓時間目標之外,還可以通過JVM參數-XX:GCPauseIntervalMillis指定停頓的間隔時長,默認是0。

聽了我的回答后,面試官對我會心一笑,我仿佛還在她的眼神中看到了一絲傾慕。正所謂:金風玉露一相逢,便勝卻人間無數,欲知后事如何,且聽下回分解。

文章持續更新,微信搜索「萬貓學社」第一時間閱讀。
關注后回復「電子書」,免費獲取12本Java必讀技術書籍。

微信公眾號:萬貓學社

微信掃描二維碼

關注后回復「電子書」

獲取12本Java必讀技術書籍


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM