JVM垃圾收集器(1)
此文已由作者趙計剛薪授權網易雲社區發布。
歡迎訪問網易雲社區,了解更多網易技術產品運營經驗。
說明:垃圾回收算法是理論,垃圾收集器是回收算法的實現,關於回收算法,見《第四章 JVM垃圾回收算法》
1、七種垃圾收集器
Serial(串行GC)-- 復制
ParNew(並行GC)-- 復制
Parallel Scavenge(並行回收GC)-- 復制
Serial Old(MSC)(串行GC)-- 標記-整理
CMS(並發GC)-- 標記-清除
Parallel Old(並行GC)--標記-整理
G1(JDK1.7update14才可以正式商用)
說明:
1~3用於年輕代垃圾回收:年輕代的垃圾回收稱為minor GC
4~6用於年老代垃圾回收(當然也可以用於方法區的回收):年老代的垃圾回收稱為full GC
G1獨立完成"分代垃圾回收"
註意:並行與並發
並行:多條垃圾回收線程同時操作
並發:垃圾回收線程與用戶線程一起操作
2、常用五種組合
Serial/Serial Old
ParNew/Serial Old:與上邊相比,只是比年輕代多了多線程垃圾回收而已
ParNew/CMS:當下比較高效的組合
Parallel Scavenge/Parallel Old:自動管理的組合
G1:最先進的收集器,但是需要JDK1.7update14以上
2.1、Serial/Serial Old:
特點:
年輕代Serial收集器采用單個GC線程實現"復制"算法(包括掃描、復制)
年老代Serial Old收集器采用單個GC線程實現"標記-整理"算法
Serial與Serial Old都會暫停所有用戶線程(即STW)
說明:
STW(stop the world):編譯代碼時為每一個方法註入safepoint(方法中循環結束的點、方法執行結束的點),在暫停應用時,需要等待所有的用戶線程進入safepoint,之後暫停所有線程,然後進行垃圾回收。
適用場合:
CPU核數<2,物理內存<2G的機器(簡單來講,單CPU,新生代空間較小且對STW時間要求不高的情況下使用)
-XX:UseSerialGC:強制使用該GC組合
-XX:PrintGCApplicationStoppedTime:查看STW時間
2.2、ParNew/Serial Old:
說明:
ParNew除了采用多GC線程來實現復制算法以外,其他都與Serial一樣,但是此組合中的Serial Old又是一個單GC線程,所以該組合是一個比較尷尬的組合,在單CPU情況下沒有Serial/Serial Old速度快(因為ParNew多線程需要切換),在多CPU情況下又沒有之後的三種組合快(因為Serial Old是單GC線程),所以使用其實不多。
-XX:ParallelGCThreads:指定ParNew GC線程的數量,默認與CPU核數相同,該參數在於CMS GC組合時,也可能會用到
2.3、Parallel Scavenge/Parallel Old:
特點:
年輕代Parallel Scavenge收集器采用多個GC線程實現"復制"算法(包括掃描、復制)
年老代Parallel Old收集器采用多個GC線程實現"標記-整理"算法
Parallel Scavenge與Parallel Old都會暫停所有用戶線程(即STW)
說明:
吞吐量:CPU運行代碼時間/(CPU運行代碼時間+GC時間)
CMS主要註重STW的縮短(該時間越短,用戶體驗越好,所以主要用於處理很多的交互任務的情況)
Parallel Scavenge/Parallel Old主要註重吞吐量(吞吐量越大,說明CPU利用率越高,所以主要用於處理很多的CPU計算任務而用戶交互任務較少的情況)
參數設置:
-XX:+UseParallelOldGC:使用該GC組合
-XX:GCTimeRatio:直接設置吞吐量大小,假設設為19,則允許的最大GC時間占總時間的1/(1+19),默認值為99,即1/(1+99)
-XX:MaxGCPauseMillis:最大GC停頓時間,該參數並非越小越好
-XX:+UseAdaptiveSizePolicy:開啟該參數,-Xmn/-XX:SurvivorRatio/-XX:PretenureSizeThreshold這些參數就不起作用了,虛擬機會自動收集監控信息,動態調整這些參數以提供最合適的的停頓時間或者最大的吞吐量(GC自適應調節策略),而我們需要設置的就是-Xmx,-XX:+UseParallelOldGC或-XX:GCTimeRatio兩個參數就好(當然-Xms也指定上與-Xmx相同就好)
註意:
-XX:GCTimeRatio和-XX:MaxGCPauseMillis設置一個就好
不開啟-XX:+UseAdaptiveSizePolicy,-Xmn/-XX:SurvivorRatio/-XX:PretenureSizeThreshold這些參數依舊可以配置,以resin服務器為例
<jvm-arg>-Xms2048m</jvm-arg> <jvm-arg>-Xmx2048m</jvm-arg> <jvm-arg>-Xmn512m</jvm-arg> <jvm-arg>-Xss1m</jvm-arg> <jvm-arg>-XX:PermSize=256M</jvm-arg> <jvm-arg>-XX:MaxPermSize=256M</jvm-arg> <jvm-arg>-XX:SurvivorRatio=8</jvm-arg> <jvm-arg>-XX:MaxTenuringThreshold=15</jvm-arg> <jvm-arg>-XX:+UseParallelOldGC</jvm-arg> <jvm-arg>-XX:GCTimeRatio=19</jvm-arg> <jvm-arg>-XX:+PrintGCDetails</jvm-arg> <jvm-arg>-XX:+PrintGCTimeStamps</jvm-arg>
適用場合:
很多的CPU計算任務而用戶交互任務較少的情況
不想自己去過多的關註GC參數,想讓虛擬機自己進行調優工作
2.4、ParNew/CMS
說明:
以上只是年老代CMS收集的過程,年輕代ParNew看"2.2、ParNew/Serial Old"就好
CMS是多回收線程的,不要被上圖誤導,默認的線程數:(CPU數量+3)/4
CMS主要註重STW的縮短(該時間越短,用戶體驗越好,所以主要用於處理很多的交互任務的情況)
特點:
年輕代ParNew收集器采用多個GC線程實現"復制"算法(包括掃描、復制)
年老代CMS收集器采用多線程實現"標記-清除"算法
初始標記:標記與根集合節點直接關聯的節點。時間非常短,需要STW
並發標記:遍歷之前標記到的關聯節點,繼續向下標記所有存活節點。時間較長。
重新標記:重新遍歷trace並發期間修改過的引用關系對象。時間介於初始標記與並發標記之間,通常不會很長。需要STW
並發清理:直接清除非存活對象,清理之後,將該線程占用的CPU切換給用戶線程
初始標記與重新標記都會暫停所有用戶線程(即STW),但是時間較短;並發標記與並發清理時間較長,但是不需要STW
關於並發標記期間怎樣記錄發生變動的引用關系對象,在重新標記期間怎樣掃描這些對象,見《第六章 JVM垃圾收集器(2)》
缺點:
並發標記與並發清理:按照說明的第二點來講,假設有2個CPU,那麽其中有一個CPU會用於垃圾回收,而另一個用於用戶線程,這樣的話,之前是兩CPU運行用戶線程,現在是一個,那麽效率就會急劇下降。也就是說,降低了吞吐量(即降低了CPU使用率)。
並發清理:在這一過程中,產生的垃圾無法被清理(因為發生在重新標記之後)
並發標記與並發清理:由於是與用戶線程並發的,所以用戶線程可能會分配對象,這樣既可能對象直接進入年老代(例如,大對象),也可能進入年輕代後,年輕代發生minor GC,這樣的話,實際上要求我們的年老代需要預留一定空間,也就是說要在年老代還有一定空間的情況下就要進行垃圾回收,留出一定內存空間來供其他線程使用,而不能等到年老代快爆滿了才進行垃圾回收,通過-XX:CMSInitiatingOccupancyFraction來指定當年老代空間滿了多少後進行垃圾回收,如果在回收過程中,老年代已經不夠使用了,這時候CMS回收失敗,老年代使用serial Old進行GC
標記-清理算法:會產生內存碎片,由於是在老年代,可能會提前觸發Full GC(這正是我們要盡量減少的)
參數設置:
-XX:+UseConcMarkSweepGC:使用該GC組合
-XX:CMSInitiatingOccupancyFraction:指定當年老代空間滿了多少後進行垃圾回收
-XX:+UseCMSCompactAtFullCollection:(默認是開啟的)在CMS收集器頂不住要進行FullGC時開啟內存碎片整理過程,該過程需要STW
-XX:CMSFullGCsBeforeCompaction:指定多少次FullGC後才進行整理
-XX:ParallelCMSThreads:指定CMS回收線程的數量,默認為:(CPU數量+3)/4
適用場合:
用於處理很多的交互任務的情況
方法區的回收一般使用CMS,配置兩個參數:-XX:+CMSPermGenSweepingEnabled與-XX:+CMSClassUnloadingEnabled
3、一些經驗
由於當下大型企業用的比較多的還是jdk1.6版本,所以G1用的還是不多
用得最多的兩種:ParNew/CMS和Parallel Scavenge/Parallel Old
Full GC的四種情況
增大survivor區
增大年老區
調低-XX:CMSInitiatingOccupancyFraction
設置:-XX:CMSMaxAbortablePrecleanTime=5(單位:ms),防止CMS在重新標記很久後才進行並發清理
增大方法區
使用CMS GC回收方法區
不要創建過大的對象獲數組
盡量讓對象在minor GC被回收
讓對象在年輕代多存活一段時間,可能這段時間內就會被minor GC回收
舊生代空間不足
方法區滿了
CMS GC中promotion failed(minor GC時,survivor區放不下,年老區也放不下)和concurrent mode failure
空間擔保機制(這一塊兒見《深入理解Java虛擬機(第二版)》P98)
附:具體的配置參數查看《深入理解Java虛擬機(第二版)》P90
免費領取驗證碼、內容安全、短信發送、直播點播體驗包及雲服務器等套餐
更多網易技術、產品、運營經驗分享請點擊。
相關文章:
【推薦】 Spring Boot 學習系列(10)—SpringBoot+JSP的使
【推薦】 3分鐘掌握一個有數小技能:回頭客分析
【推薦】 聊一聊整車廠的那些事——售後配件業務
JVM垃圾收集器(1)