1. 程式人生 > 程式設計 >一張圖讓你看懂JVM之垃圾回收器詳解

一張圖讓你看懂JVM之垃圾回收器詳解

前言

感謝讀者的反饋,在?圖中更新了新生代Eden區以及兩個Survivor區的預設空間佔比的分配表示,這裡按照10等份區分8/10、1/10、1/10分別表示8:1:1的關係,會更清晰點。新生代所採用的**“標記-複製-清除”的演演算法進行垃圾回收,以及分代回收演演算法中老年代如何為這種空間分配比所提供擔保策略,**在前文《一張圖讓你看懂JVM之垃圾回收演演算法詳解》中有比較詳細的敘述,大家可以看下,這裡就不再贅述。

另外一個遺留問題是關於direct memoryn****ative momery區別的問題,首先可以確認的是這兩種稱呼的記憶體肯定都是堆外記憶體,是不受JVM自動垃圾回收機制管理的。

從更為細緻的角度區分,目前有一種解釋是這樣表述的:"direct memory,在Java的上下文中特指Java程式通過一組特定的API訪問native memory,而這組API主要是通過DirectByteBuffer暴露出來的。而Native memory則是一個相對通用的概念,因為在Hotspot VM中,不受GC管理的記憶體都是native memory,direct memory只限定在特定的訪問Native memory的做法,二者並不完全等價"。關於這個兩個概念的解釋,如果有更好的區分大家可以給我留言,這個問題實際上並不關鍵,只是有同學有疑問,這裡就稍微解釋下了。

回到本文的主題,在之前文章中我們對Java垃圾收集演演算法的特點有了一定的認識,但是對於JVM而言演演算法並不是實現,要進行GC操作JVM是通過特定的垃圾回收器來完成的,在?圖中已經把目前比較常用的垃圾回收器,以及這些回收器分別適用於那些記憶體空間作了一個初步的展示,但是這些垃圾回收器的特點是什麼?有什麼樣的優缺點?在本文中和大家探討下。

垃圾回收器

垃圾收集器是垃圾收集演演算法的具體實現,由於Java虛擬機器器規範並沒有對垃圾收集器如何實現進行明確的規定,所以不同的JVM廠商以及不同虛擬機器器版本所提供的垃圾收集器都可能會有較大的差別,並且一般也都會提供對應的引數,供使用者根據自己的應用特點組合出各個記憶體區塊,主要是新生代、老年代所使用的收集器。

本文以JDK1.7 Update14之後的Hotspot虛擬機器器,也是目前使用最為廣泛的虛擬機器器為基礎,和大家一起看看都有那些垃圾收集器,以及它們的特點是什麼。

在詳細討論具體垃圾收集器之前,我們先看下這些收集器所適用的範圍,以及它們的組合配對關係。

具體如下圖所示:

上圖展示了JDK1.7+後,Hotspot JVM的所有垃圾收集器以及它們適用的**“代”**,適合新生代的垃圾收集器有:Serial、ParNew、Parallel Scavenge、G1

。適合年老代的垃圾收集器有:CMS、Serial Old、Parallel Old、G1。它們之間的組合關係如上圖連線(粗線相連的是最佳組合),其中G1是JDK1.7 Update14這個版本中正式提供的商業收集器,它可以同時適用於新生代和年老代。

實踐上在JDK1.7以後其實是可以統一採用G1收集器的(作者目前所在公司已經全部使用G1收集器),至於G1具體的特性,在本文稍後的內容中詳細講解。

- JAVA_OPTS=-Xms4g -Xmx4g -Xmn2g -XX:MaxDirectMemorySize=1g -XX:-
OmitStackTraceInFastThrow -XX:+UseG1GC -XX:G1ReservePercent=25 -
XX:InitiatingHeapOccupancyPercent=30 -XX:SoftRefLRUPolicyMSPerMB=0 -XX:SurvivorRatio=8 -
XX:+DisableExplicitGC -verbose:gc -Xloggc:/opt/gc_%p.log -XX:+PrintGCDetails -
XX:+PrintGCDateStamps -XX:+PrintGCApplicationStoppedTime -XX:+PrintAdaptiveSizePolicy -
XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=5 -XX:GCLogFileSize=30m
複製程式碼

雖然實踐上已然可以同一採用G1了,但是作為基礎知識,我們還是對這些具體垃圾收集器有一個比較深刻的認識會比較好。下面就依次討論下這些垃圾收集器的特點是什麼吧!

Serial(新生代-序列-收集器)

Serial是一個比較古老的收集器,而且是一個單執行緒的收集器,這種收集器在進行垃圾收集時,必須暫停其他所有的工作執行緒,直到它收集結束(採用的是複製演演算法)。所以試想下在網際網路高併發的場景下采用這樣的收集器顯然是不可以的,所以我們目前在做後端服務時,不可能會用到這款收集器。

因為其簡單、單執行緒的特點用在Java桌面程式場景會比較合適,但是大家也都知道目前採用Java開發桌面程式已經很少見了,所以大家對此瞭解就行。

其工作示意圖如下:

ParNew(新生代-並行-收集器)

Parnew收集器是Serial的多執行緒版本,除了多執行緒收集外,其餘部分與Serial相比並沒有多大的差別,由於其可以與CMS收集器配合使用,所以在JDK1.7之前,對於Java服務應用來說是首選的新生代收集器。

示意圖如下:

這種收集器與Serial一樣,在進行垃圾回收時也會暫停所有使用者工作執行緒,只是它採取了多執行緒回收,所以回收的速度會比Serial快,從而將卡頓時間縮短。

Parallel Scavenge (新生代-並行-收集器)

Parallel Scavenge也是一個新生代收集器,採用的也是多執行緒,以及複製收集演演算法,與其他同型別收集器不同的是,它的關注點是達到一個可控制吞吐量的目標,***吞吐量=執行使用者程式碼時間/(執行使用者程式碼時間+垃圾回收的時間),***假設虛擬機器器總共運行了100分鐘,其中垃圾回收花了一分鐘,那麼吞吐量就是99%。高吞吐量的目的是為了高效的利用CPU時間,從而儘快的完成程式運算任務,主要適合後臺運算不需要有太多互動的應用場景。

該收集器提供的控制引數有:

• MaxGCPauseMillis:GC時間的最大值。

• GCTimeRatio:GC時間佔用總時間的比例。

• UseAdaptiveSizePolicy:這個引數則是開啟GC記憶體分配的自適應調整策略。可以自動調節,新生代的大小、Eden與Survivor的比例、晉升老年代物件的年齡。

Serial Old(年老代-序列-收集器)

Serial Old 收集器就是Serial的老年代版本,是一個單執行緒的,垃圾收集演演算法採用標記-整理演演算法的收集器。

Parallel Old(年老代-並行-收集器)

Parallel Old收集器是Parallel Scavenge的老年代版本,是多執行緒,採用標記-整理演演算法的收集器。

CMS(年老代-並行-收集器)

CMS(Concurrent Mark-Sweep)是一款以獲取最短回收停頓時間為目標的收集器。比較適合網際網路響應式應用場景,採用的是**“標記-清除”**演演算法。

其收集過程如下圖所示:

如圖所示,在初始標記和重新標記兩個步驟,也會和Serial 一樣暫停所有使用者執行緒。

G1收集器

G1(Garbage First)是目前最為前沿的垃圾回收器,在前面的內容中已經提過JDK1.8以後的生產實踐新生代、年老代都可以採用G1作為垃圾回收器。其採用的收集演演算法是”標記-清除-整理“,所以不會產生記憶體碎片。

後記

在實踐中關於GC收集器的選擇,看目前來看,如果是網際網路高併發場景的伺服器端應用,可以統一使用G1就行。而如果是其他的應用場景,可能就需要根據合適的場景進行組合選擇了。