1. 程式人生 > 程式設計 >面試官:怎麼做JDK8的垃圾收集器的調優(面試常問)

面試官:怎麼做JDK8的垃圾收集器的調優(面試常問)

面試官:怎麼做JDK8的垃圾收集器的調優(面試常問)

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

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

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

Serial收集器

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

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

面試官:怎麼做JDK8的垃圾收集器的調優(面試常問)

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

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

Parallel收集器

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

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

面試官:怎麼做JDK8的垃圾收集器的調優(面試常問)

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收集器。看著面試官期待的眼神,我又詳細解釋到:

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):這個步驟耗時較長,但使用者執行緒可同時執行。

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

面試官:怎麼做JDK8的垃圾收集器的調優(面試常問)

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

G1收集器

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

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

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

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

面試官:怎麼做JDK8的垃圾收集器的調優(面試常問)

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

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

總結

到此這篇關於面試官:怎麼做JDK8的垃圾收集器的調優(面試常問)的文章就介紹到這了,更多相關JDK8的垃圾收集器的調優內容請搜尋我們以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援我們!