JVM中的垃圾回收器

1. 收集器概覽

Oracle Hotspot JVM中實現了多種垃圾收集器,針對不同的年齡代內存中的對象的生存週期和應用程序的特點,實現了多款垃圾收集器。
單線程GC收集器包括Serial和SerialOld這兩款收集器,分別用於年輕代和老年代的垃圾收集工作。後來,隨着CPU多核的普及,爲了更好了利用多核的優勢,開發了ParNew收集器,這款收集器是Serial收集器的多線程版本。
多線程收集器還包括Parallel Scavenge和ParallelOld收集器,這兩款也分別用於年輕代和老年代的垃圾收集工作,不同的是,它們是兩款可以利用多核優勢的多線程收集器。
相對來說更加複雜的還有CMS收集器。這款收集器,在運行的時候會分多個階段進行垃圾收集,而且在一些階段是可以和應用線程並行運行的,提高了這款收集器的收集效率。
其中最先進的收集器,要數G1這款收集器了。這款收集器是當前最新發布的收集器,是一款面向服務端垃圾收集器。
接下來,我們來分別介紹下上面提到的那些GC收集器以及它們各自的特點。

2. 年輕代收集器

年輕代收集器包括Serial收集器、ParNew收集器以及Parallel Scavenge收集器。

2.1 Serial收集器

Serial收集器是一款年輕代的垃圾收集器,使用標記-複製垃圾收集算法。它是一款發展歷史最悠久的垃圾收集器。Serial收集器只能使用一條線程進行垃圾收集工作,並且在進行垃圾收集的時候,所有的工作線程都需要停止工作,等待垃圾收集線程完成以後,其他線程纔可以繼續工作。工作過程可以簡單的用下圖來表示:
在這裏插入圖片描述

從圖中可以看到,Serial收集器工作的時候,其他用戶線程都停止下來,等到GC過程結束以後,它們才繼續執行。而且處理GC過程的只有一條線程在執行。由於Serial收集器的這種工作機制,所以在進行垃圾收集過程中,會出現STW(Stop The World)的情況,應用程序會出現停頓的狀況。如果垃圾收集的時間很長,那麼停頓時間也會很長,這樣會導致系統響應變的遲鈍,影響系統的時候。
雖然這款年邁的垃圾收集器只能使用單核CPU,但是正是由於它不能利用多核,在一些場景下,減少了很多線程的上下文切換的開銷,可以在進行垃圾收集過程中專心處理GC過程,而不會被打斷,所以如果GC過程很短暫,那麼這款收集器還是非常簡單高效的。
由於Serial收集器只能使用單核CPU,在現代處理器基本都是多核多線程的情況下,爲了充分利用多核的優勢,出現了多線程版本的垃圾收集器,比如下面將要說到的ParNew收集器。

2.2 ParNew收集器

ParNew垃圾收集器是Serial收集器的多線程版本,使用標記-複製垃圾收集算法。爲了利用CPU多核多線程的優勢,ParNew收集器可以運行多個收集線程來進行垃圾收集工作。這樣可以提高垃圾收集過程的效率。
在這裏插入圖片描述
和上面的Serial收集器比較,可以明顯看到,在垃圾收集過程中,GC線程是多線程執行的,而在Serial收集器中,只有一個GC線程在處理垃圾收集過程。ParNew收集器在很多時候都是作爲服務端的年輕代收集器的選擇,除了它具有比Serial收集器更好的性能外,還有一個原因是,多線程版本的年輕代收集器中,只有它可以和CMS這款優秀的老年代收集器一起搭配搭配使用。
作爲一款多線程收集器,當它運行在單CPU的機器上的時候,由於不能利用多核的優勢,在線程收集過程中可能會出現頻繁上下文切換,導致額外的開銷,所以在單CPU的機器上,ParNew收集器的性能不一定好於Serial這款單線程收集器。如果機器是多CPU的,那麼ParNew還是可以很好的提高GC收集的效率的。
ParNew收集器默認開啓的垃圾收集線程數是和當前機器的CPU數量相同的,爲了控制GC收集線程的數量,可以通過參數-XX:ParallelGCThreads來控制垃圾收集線程的數量。

2.3 Parallel Scavenge收集器

Parallel Scavenge收集器是是一款年輕代的收集器,它使用標記-複製垃圾收集算法。和ParNew一樣,它也會一款多線程的垃圾收集器,但是它又和ParNew有很大的不同點。
Parallel Scavenge收集器和其他收集器的關注點不同。其他收集器,比如ParNew和CMS這些收集器,它們主要關注的是如何縮短垃圾收集的時間。而Parallel Scavenge收集器關注的是如何控制系統運行的吞吐量。這裏說的吞吐量,指的是CPU用於運行應用程序的時間和CPU總時間的佔比:
吞吐量 = 代碼運行時間 / (代碼運行時間 + 垃圾收集時間)
如果虛擬機運行的總的CPU時間是100分鐘,而用於執行垃圾收集的時間爲1分鐘,那麼吞吐量就是99%。
在這裏插入圖片描述
直觀上,好像以縮短垃圾收集的停頓時間爲目的和以控制吞吐量爲目的差不多,但是適用的場景卻不同。對於那些桌面應用程序,爲了得到良好的用戶體驗,在交互過程中,需要得到快速的響應,所以系統的停頓時間要儘可能的快以避免影響到系統的響應速度,只要保證每次停頓的時間很短暫,假設每次停頓時間爲10ms,那麼即使發生很多次的垃圾收集過程,假設1000次,也不會影響到系統的響應速度,不會影響到用戶的體驗。對於一些後臺計算任務,它不需要和用戶進行交互,所以短暫的停頓時間對它而言並不需要,對於計算任務而言,更好的利用CPU時間,提高計算效率纔是需要的,所以假設每次停頓時間相對很長,有100ms,而由於花費了很長的時間進行垃圾收集,那麼垃圾收集的次數就會降下來,假設只有5次,那麼顯然,使用以吞吐量爲目的的垃圾收集器,可以更加有效的利用CPU來完成計算任務。所以,在用戶界面程序中,使用低延遲的垃圾收集器會有很好的效果,而對於後臺計算任務的系統,高吞吐量的收集器纔是首選。

Parallel Scavenge收集器提供了兩個參數用於控制吞吐量。-XX:MaxGCPauseMillis用於控制最大垃圾收集停頓時間,-XX:GCTimeRatio用於直接控制吞吐量的大小。MaxGCPauseMillis參數的值允許是一個大於0的整數,表示毫秒數,收集器會儘可能的保證每次垃圾收集耗費的時間不超過這個設定值。但是如果這個這個值設定的過小,那麼Parallel Scavenge收集器爲了保證每次垃圾收集的時間不超過這個限定值,會導致垃圾收集的次數增加和增加年輕代的空間大小,垃圾收集的吞吐量也會隨之下降。GCTimeRatio這個參數的值應該是一個0-100之間的整數,表示應用程序運行時間和垃圾收集時間的比值。如果把值設置爲19,即系統運行時間 : GC收集時間 = 19 : 1,那麼GC收集時間就佔用了總時間的5%(1 / (19 + 1) = 5%),該參數的默認值爲99,即最大允許1%(1 / (1 + 99) = 1%)的垃圾收集時間。
Parallel Scavenge收集器還有一個參數:-XX:UseAdaptiveSizePolicy。這是一個開關參數,當開啓這個參數以後,就不需要手動指定新生代的內存大小(-Xmn)、Eden區和Survivor區的比值(-XX:SurvivorRatio)以及晉升到老年代的對象的大小(-XX:PretenureSizeThreshold)等參數了,虛擬機會根據當前系統的運行情況動態調整合適的設置值來達到合適的停頓時間和合適的吞吐量,這種方式稱爲GC自適應調節策略。
Parallel Scavenge收集器也是一款多線程收集器,但是由於目的是爲了控制系統的吞吐量,所以這款收集器也被稱爲吞吐量優先收集器。

3. 老年代收集器

老年代收集包括:Serial Old收集器、Parallel Old收集器以及CMS收集器。

3.1 Serial Old收集器

Serial Old收集器是Serial收集器的老年代版本,它也是一款使用標記-整理算法的單線程的垃圾收集器。這款收集器主要用於客戶端應用程序中作爲老年代的垃圾收集器,也可以作爲服務端應用程序的垃圾收集器,當它用於服務端應用系統中的時候,主要是在JDK1.5版本之前和Parallel Scavenge年輕代收集器配合使用,或者作爲CMS收集器的後備收集器。
在這裏插入圖片描述

3.2 Parallel Old收集器

Parallel Old收集器是Parallel Scavenge收集器的老年代版本,使用標記-整理算法。這個收集器是在JDK1.6版本中出現的,所以在JDK1.6之前,新生代的Parallel Scavenge只能和Serial Old這款單線程的老年代收集器配合使用。Parallel Old垃圾收集器和Parallel Scavenge收集器一樣,也是一款關注吞吐量的垃圾收集器,和Parallel Scavenge收集器一起配合,可以實現對Java堆內存的吞吐量優先的垃圾收集策略。
Parallel Old垃圾收集器的工作原理和Parallel Scavenge收集器類似。
在這裏插入圖片描述

3.3 CMS收集器

CMS收集器是目前老年代收集器中比較優秀的垃圾收集器。CMS是Concurrent Mark Sweep,從名字可以看出,這是一款使用標記-清除算法的併發收集器。CMS
垃圾收集器是一款以獲取最短停頓時間爲目標的收集器。由於現代互聯網中的應用,比較重視服務的響應速度和系統的停頓時間,所以CMS收集器非常適合在這種場景下使用。
CMS收集器的運行過程相對上面提到的幾款收集器要複雜一些。
在這裏插入圖片描述
從圖中可以看出,CMS收集器的工作過程可以分爲4個階段:

  • 初始標記(CMS initial mark)階段
  • 併發標記(CMS concurrent mark)階段
  • 重新標記(CMS remark)階段
  • 併發清除(CMS concurrent sweep)階段

從圖中可以看出,在這4個階段中,初始標記和重新標記這兩個階段都是隻有GC線程在運行,用戶線程會被停止,所以這兩個階段會發生STW(Stop The World)。初始標記階段的工作是標記GC Roots可以直接關聯到的對象,速度很快。併發標記階段,會從GC Roots 出發,標記處所有可達的對象,這個過程可能會花費相對比較長的時間,但是由於在這個階段,GC線程和用戶線程是可以一起運行的,所以即使標記過程比較耗時,也不會影響到系統的運行。重新標記階段,是對併發標記期間因用戶程序運行而導致標記變動的那部分記錄進行修正,重新標記階段耗時一般比初始標記稍長,但是遠小於併發標記階段。最終,會進行併發清理階段,和併發標記階段類似,併發清理階段不會停止系統的運行,所以即使相對耗時,也不會對系統運行產生大的影響。
由於併發標記和併發清理階段是和應用系統一起執行的,而初始標記和重新標記相對來說耗時很短,所以可以認爲CMS收集器在運行過程中,是和應用程序是併發執行的。由於CMS收集器是一款併發收集和低停頓的垃圾收集器,所以CMS收集器也被稱爲併發低停頓收集器。
雖然CMS收集器可以是實現低延遲併發收集,但是也存在一些不足。
首先,CMS收集器對CPU資源非常敏感。對於併發實現的收集器而言,雖然可以利用多核優勢提高垃圾收集的效率,但是由於收集器在運行過程中會佔用一部分的線程,這些線程會佔用CPU資源,所以會影響到應用系統的運行,會導致系統總的吞吐量降低。CMS默認開始的回收線程數是(Ncpu + 3) / 4,其中Ncpu是機器的CPU數。所以,當機器的CPU數量爲4個以上的時候,垃圾回收線程將佔用不少於%25的CPU資源,並且隨着CPU數量的增加,垃圾回收線程佔用的CPU資源會減少。但是,當CPU資源少於4個的時候,垃圾回收線程佔用的CPU資源的比例會增大,會影響到系統的運行,假設有2個CPU的情況下,垃圾回收線程將會佔據超過50%的CPU資源。所以,在選用CMS收集器的時候,需要考慮,當前的應用系統,是否對CPU資源敏感。
其次,CMS收集器在處理垃圾收集的過程中,可能會產生浮動垃圾,由於它無法處理浮動垃圾,所以可能會出現Concurrent Mode Failure問題而導致觸發一次Full GC。所謂的浮動垃圾,是由於CMS收集器的併發清理階段,清理線程是和用戶線程一起運行,如果在清理過程中,用戶線程產生了垃圾對象,由於過了標記階段,所以這些垃圾對象就成爲了浮動垃圾,CMS無法在當前垃圾收集過程中集中處理這些垃圾對象。由於這個原因,CMS收集器不能像其他收集器那樣等到完全填滿了老年代以後才進行垃圾收集,需要預留一部分空間來保證當出現浮動垃圾的時候可以有空間存放這些垃圾對象。在JDK 1.5中,默認當老年代使用了68%的時候會激活垃圾收集,這是一個保守的設置,如果在應用中老年代增長不是很快,可以通過參數-XX:CMSInitiatingOccupancyFraction控制觸發的百分比,以便降低內存回收次數來提供性能。在JDK 1.6中,CMS收集器的激活閥值變成了92%。如果在CMS運行期間沒有足夠的內存來存放浮動垃圾,那麼就會導致Concurrent Mode Failure失敗,這個時候,虛擬機將啓動後備預案,臨時啓動Serial Old收集器來對老年代重新進行垃圾收集,這樣會導致垃圾收集的時間邊長,特別是當老年代內存很大的時候。所以對參數-XX:CMSInitiatingOccupancyFraction的設置,過高,會導致發生Concurrent Mode Failure,過低,則浪費內存空間。
CMS的最後一個問題,就是它在進行垃圾收集時使用的標記-清除算法,會出現很多內存碎片,過多的內存碎片會影響大對象的分配,會導致即使老年代內存還有很多空閒,但是由於過多的內存碎片,不得不提前觸發垃圾回收。爲了解決這個問題,CMS收集器提供了一個-XX:+UseCMSCompactAtFullCollection參數,用於CMS收集器在必要的時候對內存碎片進行壓縮整理。由於內存碎片整理過程不是併發的,所以會導致停頓時間變長。-XX:+UseCMSCompactAtFullCollection參數默認是開啓的。虛擬機還提供了一個-XX:CMSFullGCsBeforeCompaction參數,來控制進行過多少次不壓縮的Full GC以後,進行一次帶壓縮的Full GC,默認值是0,表示每次在進行Full GC前都進行碎片整理。
雖然CMS收集器存在上面提到的這些問題,但是毫無疑問,CMS當前仍然是非常優秀的垃圾收集器。

4. G1收集器

G1(Garbage First)垃圾收集器是當今垃圾回收技術最前沿的成果之一。早在JDK7就已加入JVM的收集器大家庭中,成爲HotSpot重點發展的垃圾回收技術。同優秀的CMS垃圾回收器一樣,G1也是關注最小時延的垃圾回收器,也同樣適合大尺寸堆內存的垃圾收集,官方也推薦使用G1來代替選擇CMS。G1最大的特點是引入分區的思路,弱化了分代的概念,合理利用垃圾收集各個週期的資源,解決了其他收集器甚至CMS的衆多缺陷。

G1收集與前面介紹的收集器有很大不同:

  • G1的設計原則是"首先收集儘可能多的垃圾(Garbage First)"。因此,G1並不會等內存耗盡(串行、並行)或者快耗盡(CMS)的時候開始垃圾收集,而是在內部採用了啓發式算法,在老年代找出具有高收集收益的分區進行收集。同時G1可以根據用戶設置的暫停時間目標自動調整年輕代和總堆大小,暫停目標越短年輕代空間越小、總空間就越大;
  • G1採用內存分區(Region)的思路,將內存劃分爲一個個相等大小的內存分區,回收時則以分區爲單位進行回收,存活的對象複製到另一個空閒分區中。由於都是以相等大小的分區爲單位進行操作,因此G1天然就是一種壓縮方案(局部壓縮);
  • G1雖然也是分代收集器,但整個內存分區不存在物理上的年輕代與老年代的區別,也不需要完全獨立的survivor(to space)堆做複製準備。G1只有邏輯上的分代概念,或者說每個分區都可能隨G1的運行在不同代之間前後切換;
  • G1的收集都是STW的,但年輕代和老年代的收集界限比較模糊,採用了mixed gc的方式。即每次收集既可能只收集年輕代分區(年輕代收集),也可能在收集年輕代的同時,包含部分老年代分區(混合收集),這樣即使堆內存很大時,也可以限制收集範圍,從而降低停頓。

Region區域劃分與其他收集類似,不同的是單獨將大對象分配到了單獨的region中,會分配一組連續的Region區域(Humongous start 和 humonous Contoinue 組成),所以一共有四類Region(Eden,Survior,Humongous和Old):
在這裏插入圖片描述
G1收集器之所以能建立可預測的停頓時間模型,是因爲它可以有計劃地避免在整個Java堆中進行全區域的垃圾收集。G1跟蹤各個Region裏面的垃圾堆積的價值大小(回收所獲得的空間大小以及回收所需時間的經驗值),在後臺維護一個優先列表,每次根據允許的收集時間,優先回收價值最大的Region(這也就是Garbage-First名稱的來由)。這種使用Region劃分內存空間以及有優先級的區域回收方式,保證了G1收集器在有限的時間內可以獲取儘可能高的收集效率。
G1收集器的運作大致可劃分爲以下幾個步驟:

  • 初始標記(Initial Marking)
    初始標記階段僅僅只是標記一下GC Roots能直接關聯到的對象,並且修改TAMS(Next Top at Mark Start)的值,讓下一階段用戶程序併發運行時,能在正確可用的Region中創建新對象,這階段需要停頓線程,但耗時很短。
  • 併發標記(Concurrent Marking)
    併發標記階段是從GC Root開始對堆中對象進行可達性分析,找出存活的對象,這階段耗時較長,但可與用戶程序併發執行。
  • 最終標記(Final Marking)
    最終標記階段是爲了修正在併發標記期間因用戶程序繼續運作而導致標記產生變動的那一部分標記記錄,虛擬機將這段時間對象變化記錄在線程Remembered Set Logs裏面,最終標記階段需要把Remembered Set Logs的數據合併到Remembered Set中,這階段需要停頓線程,但是可並行執行。
  • 篩選回收(Live Data Counting and Evacuation)
    篩選回收階段首先對各個Region的回收價值和成本進行排序,根據用戶所期望的GC停頓時間來制定回收計劃,這個階段其實也可以做到與用戶程序一起併發執行,但是因爲只回收一部分價值高的Region區的垃圾對象,時間是用戶可控制的,而且停頓用戶線程將大幅提高收集效率。回收時,採用“複製”算法,從一個或多個Region複製存活對象到堆上的另一個空的Region,並且在此過程中壓縮和釋放內存。

G1部分設置參數如下:
-XX:+UseG1GC:指定使用G1收集器;
-XX:InitiatingHeapOccupancyPercent:當整個Java堆的佔用率達到參數值時,開始併發標記階段;默認爲45;
-XX:MaxGCPauseMillis:爲G1設置暫停時間目標,默認值爲200毫秒;
-XX:G1HeapRegionSize:設置每個Region大小,範圍1MB到32MB;目標是在最小Java堆時可以擁有約2048個

5. 垃圾收集器總結

GC組合 Minor GC Full GC 描述
-XX:+UseSerialGC Serial收集器串行回收 Serial Old收集器串行回收 該選項可以手動指定Serial收集器+Serial Old收集器組合執行內存回收

6. GC日誌分析

垃圾收集器在進行垃圾收集的過程中,可以輸出日誌,我們通過日誌,可以看到當前垃圾收集器的運行情況。通過gc日誌,我們可以觀察垃圾收集器的行爲,以及當前應用程序的GC情況和內存使用情況。學會查看和分析垃圾收集日誌,一方面可以幫助我們學習垃圾收集器;另一方面,在必要的時候,可以幫助我們定位問題,解決問題,對JVM進行優化。
默認,JVM不會打印出GC日誌信息,可以通過參數-XX:+PrintGC-verbose:gc來設置JVM輸出gc日誌到終端中。

JVM參數:-XX:+PrintGC -XX:+UseSerialGC -Xms10m -Xmx10m
在這裏插入圖片描述
當設置了-XX:+PrintGC或者-verbose:gc以後就會輸出類似輸出上面的GC日誌。這是最簡單的GC日誌,包含了垃圾收集過程中的信息。其中紅色部分的"GC"和"Full GC"表示這次GC的類型,而綠色部分的"Allocation Failure"表示表示發生這次GC的原因,從上面的日誌可以看出,是由於內存分配失敗導致的GC。後面的黃色部分"1922K->1394K(9920K)"表示這次GC導致JVM中堆內存的使用量從1922K降低到了1394K,其中括號中表示當前整個JVM堆的大小。最後藍色部分的"0.0021245 secs"表示這次GC持續的時間。
上面輸出的是簡單格式的GC日誌,雖然提供了一些信息,但是通過這些信息,我們沒法知道這次GC發生的時候,這次GC是發生在老年代還是在年輕代,是否有對象從年輕代被移動到了老年代等信息,所以我們希望可以看到更加詳盡的信息。這個時候,我們需要設置-XX:+PrintGCDetails參數來輸出更加詳細的GC日誌,下面我們結合不同的收集器組合,來分析下它們的輸出日誌。

6.1 Serial GC + Serial Old

Serial GC和Serial Old收集器是比較早的單線程收集器,工作原理我們在上面已經介紹過了。這裏,我們來看下使用這兩款收集器進行垃圾收集的時候,輸出的日誌格式是怎麼樣的。首先我們需要設置JVM參數:

JVM參數:-XX:+PrintGC -XX:+PrintGCDetails -XX:+UseSerialGC -Xms10m -Xmx10m
在這裏插入圖片描述

可以發現,通過設置了-XX:+PrintGCDetails以後,輸出的GC日誌信息多了很多。我們先來看第一條,紅色部分"GC"表示這次發生的是Minor GC,綠色部分"Allocation Failure"表示導致這次GC的原因是內存分配失敗。接下來,黃色部分的內容,則和前面的日誌有些區別了,這裏輸出的內容相對比較詳細。“DefNew: 1922K->319K(3072K), 0.0027356 secs] 1922K->1394K(9920K), 0.0027698 secs”,其中DefNew表示這次GC發生在年輕代(不同的收集器,日誌的格式不一定相同),接下來"1922K->319K"表示這次GC導致年輕代使用的內存從1922K降到319K,括號中的"3072K"表示年輕代中的堆內存大小爲3072K。"0.0027356 secs"表示這次年輕代GC耗時0.0027356s。後面的"1922K->1393K"表示總的堆內存(年輕代 + 老年代)的使用情況的變化,從1922K降低到1394K, 括號中的"9920K"表示總的堆內存的大小。最後的"0.0027698 secs"表示這次GC總的消耗的時間。最後是這次GC消耗的時間的統計,其中user表示用戶態CPU執行的時間,sys表示內核態CPU執行的時間,這兩個時間不包括被掛起消耗的時間,而real表示的是實際的時間,可以認爲是牆上時鐘走過的時間。
下面的這條日誌,"Full GC"表示這次GC是一次Major GC,後面的原因和上面一樣。我們來看下黃色部分,“Tenured"表示這次GC發生在老年代,其中"6524K->6484K"表示老年代內存從6524K降低到6484K。後面的時間"0.0025899 secs"表示這次老年代GC耗時0.0025899s。接下來的"8562K -> 8532K"和上面提到的一樣,表示整個堆內存的變化。最後的時間表示這次GC的總耗時爲"0.0026153s”。

6.2 Parallel Scanvage + Parallel Old

不同的垃圾收集器,輸出的日誌信息也不是完全相同的,上面我們看到的日誌,是使用Serial GC和Serial Old收集器輸出的gc日誌,而下面的日誌信息,則是使用Parallel Scavenge收集器和Parallel Old收集器輸出的日誌。

JVM參數:-XX:+PrintGC -XX:+UseParallelOldGC -XX:+PrintGCDetails -Xms10m -Xmx10m
在這裏插入圖片描述
可以看到,使用Parallel Scavenge 和 Parallel Old收集器輸出的日誌,會有一些不同,不過日誌內容大體上差不多。最後,我們來看下CMS垃圾收集器的日誌是怎麼樣的,相對上面幾款收集器,CMS相對更加複雜,從它輸出的日誌也可以看出來。

6.3 ParNew + Concurrent Mark Sweep(CMS)

下面,我們來看下ParNew配合CMS收集器在進行垃圾收集的時候,輸出的GC 日誌信息。

JVM參數:-XX:+PrintGC -XX:+UseConcMarkSweepGC -XX:+PrintGCDetails -Xms10m -Xmx10m
在這裏插入圖片描述
通過第一條日誌,可以看出我們使用"-XX:+UseConcMarkSweepGC"指定CMS垃圾收集器的時候,使用的是ParNew + CMS收集器組合。下面輸出的一堆日誌,就是CMS收集器在進行垃圾收集過程中輸出的信息。可以明顯的看到,CMS在進行垃圾收集的過程中,經歷了4個階段,在日誌中我用4中顏色標記出來了。需要注意的是黃色部分,這是CMS的重新標記的階段,在上面我們介紹CMS收集器的時候說過,在這個階段,是會出現Stop The World的,所以如果這個階段消耗的時間比較長,則會影響應用的響應時間。

6.4 其他日誌參數

有時候,我們需要在GC日誌中輸出時間值,這樣我們就可以知道這次GC發生的具體時間點。我們可以通過JVM參數-XX:+PrintGCDateStamps來設置日誌輸出的時間。
在這裏插入圖片描述
除了將日誌輸出到控制檯,我們還可以將日誌輸出到日誌文件中,這樣就可以通過分析日誌文件來分析系統的GC情況了,一般在服務器運行過程中,我們都會將GC日誌輸出到指定的文件中,供需要的時候分析。可以通過JVM參數-Xloggc:<file>來指定日誌輸出的目錄。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章