JVM學習和分析(二):GC
一、關於GC
GC是JAVA語言最重要的特性之一,GC為廣大JAVA程式設計師解決了記憶體管理的諸多問題,但GC是一把雙刃劍,在替程式設計師解決了記憶體管理的同時,也隱藏了很多細節,使JAVA程式設計師並不能像C程式設計師那樣對記憶體做到控制。因此,很多時候JAVA程式設計師忽略了對記憶體的管理,認為JAVA可以替程式設計師管理好記憶體,正是因為這樣,JAVA程式會出現很多莫名其妙的問題。
個人認為,JAVA程式設計師其實在寫程式的時候,本質上就是在和GC做鬥爭,儘可能的去減少GC,但又要保證GC及時有效。
二、常用的基礎GC演算法
1、標記-清除(mark-sweep)演算法
最基礎的方法,分為標記和清除兩個階段工作
缺點:效率低下,容易產生大量記憶體碎片,過多記憶體碎片會導致無法分配大物件,從而導致第二次GC
2、複製(copying)演算法
將堆記憶體分為相等的兩塊,每次將還存活的物件複製到另外一半記憶體中,將當前記憶體部分的所有物件清除
優點:解決了標記-清除演算法的效率問題
缺點:浪費了一半的記憶體空間,且在物件存活率較高時,需要較多的複製操作
3、標記-整理(mark-compact)演算法
工作方式和標記-清除演算法類似,首先標記待回收的物件,標記完後,將存活的物件都向記憶體一端移動,然後清除掉邊界外的物件
優點:避免出現過多的記憶體碎片
缺點:效率不高
4、分代收集演算法(目前使用最多的演算法)
將堆記憶體分為新生代(Eden,S0,S1)和老年代(old),然後根據不同的年代採用不同的垃圾收集演算法
三、GC收集器
GC收集器是真正負責物件回收的部分,不同的GC收集器採用了不同的演算法(組合)
1、Serial收集器
最基礎,最古老的收集器,是一個完全的單執行緒收集器。Serial收集器在工作的時候,需要阻塞所有的JAVA執行緒,直到GC工作完成
新生代:複製演算法
老年代:標記-整理演算法
優點:簡單高效,避免執行緒互動的開銷
缺點:阻塞所有程序
工作場景:client模式下的JVM垃圾收集器,一般情況下,client模式的新生代記憶體在百兆左右,Serial收集時間大約幾十毫秒
2、ParNew收集器
多執行緒版本的Serial收集器
新生代:複製演算法
老年代:標記-整理演算法
優點:簡單高效,避免執行緒互動的開銷
缺點:阻塞所有程序
工作場景:server模式下的首先新生代垃圾收集器,配合CMS(作為老年代收集器)組成完成的GC收集器
3、Parallel Scavenge收集器
為達到可控制的吞吐量而設計的新生代收集器
吞吐量=執行使用者程式碼時間/(執行使用者程式碼時間+垃圾收集時間)
+XX:MaxGCPauseMillis控制最大垃圾收集停頓時間,單位毫秒
+XX:GCTimeRatio設定吞吐量大小,大於0小於100的整數
優點:比較準確的控制吞吐量
缺點:設定不合理的時候,會導致更多的GC
新生代:複製演算法
工作場景:需要比較準確的吞吐量的場景
4、Parallel Old收集器
Parallel Scavenge的老年代版本
工作場景:在注重吞吐量及CPU資源敏感的場合,可以考慮使用Parallel Scavenge和Parallel Scavenge組合
5、CMS收集器
一個非常優秀的GC收集器,全稱Concurrent Mark Sweep,是基於標記-清除演算法,以獲取最短回收停頓時間為目標的GC收集器,收集過程包括四個階段:
- 初始標記:標記GC Roots能直接關聯的物件
- 併發標記:進行GC Roots Tracing
- 重新標記:去掉併發標記期間,因為使用者繼續使用而產生變化的物件
- 併發清除
其中,初始標記和重新標記會阻塞所有執行緒,但由於時間非常短,所以一般不會被使用者察覺
優點:快速,高效
缺點:對CPU資源敏感,無法處理浮動垃圾,會產生大量碎片
工作場景:需要高響應速度的B/S系統
6、G1收集器
目前最先進的垃圾收集器,全稱Garbage First,基於標記-整理演算法,G1收集器將堆記憶體分為若干個大小固定的區域,內部維護一個優先列表,在GC時間允許的範圍內,對優先列表中垃圾最多的區域進行回收,從而使GC收集器獲得最高的收集效率。
正式版本發行在JDK1.7
優點:收集效率高,長期執行穩定
缺點:目前沒發現
工作場景:需要高響應速度的B/S系統
四、個人經驗
CMS和G1都是非常適合應用在高訪問量的系統中,但兩者在實際使用中還是有不少差別,以下對比均為本人在同一套應用上得到
1、記憶體使用方面
使用CMS:通常穩定用到一部分堆記憶體,往往在使用一段時間後,會出現記憶體使用量跳變(到現在我還是沒想明白為什麼會出現跳變),跳變幅度一般為1倍,如果原來記憶體使用已經超過了一半,跳變後馬上會出現一次FULL GC,更嚴重的是出現OOM
使用G1:應用啟動後,瞬間會用滿所有堆記憶體,然後一直會佔用所有堆記憶體,但極少會有FULL GC出現,使用G1後,大約半年才出現了一次FULL GC
2、GC時間
使用CMS:YGC大約1-2秒一次,每次大約100ms左右
使用G1:YGC大約1秒3-4次,每次大約10ms左右
從記憶體使用和GC時間上來看,G1在高吞吐,高併發的環境下,表現還是非常不錯的