CMS收集器
CMS收集器是基於「標記—清除」算法實現,整個過程分爲4個步驟,包括:
初始標記(CMS initial mark)
併發標記(CMS concurrent mark)
從新標記(CMS remark)
併發清除(CMS concurrent sweep)
其中,初始標記、從新標記這兩個步驟仍然須要「Stop The World」。
初始標記僅僅只是標記一下GC Roots能直接關聯到的對象,速度很快
併發標記階段就是進行GC Roots Tracing的過程
從新標記階段則是爲了修正併發標記期間因用戶程序繼續運做而致使標記產生變更的那一部分對象的標記記錄,
這個階段的停頓時間通常會比初始標記階段稍長一些,但遠比並發標記的時間短。
CMS收集器沒法處理浮動垃圾(Floating Garbage),可能出現「Concurrent Mode Failure」失敗而致使另外一次Full GC的產生。
因爲CMS併發清理階段用戶線程還在運行着,伴隨程序運行天然就還會有新的垃圾不斷產生,
這一部分垃圾出如今標記過程以後,CMS沒法在當次收集中處理掉它們,只好留待下一次GC時再清理掉。
這一部分垃圾就稱爲「浮動垃圾」。
也是因爲在垃圾收集階段用戶線程還須要運行,那也就還須要預留有足夠的內存空間給用戶線程使用,
所以CMS收集器不能像其餘收集器那樣等到老年代幾乎徹底被填滿了再進行收集,須要預留一部分空間提供併發收集時的程序運做使用。
1.6中,CMS收集器的啓動閾值已經提高至92%。
要是CMS運行期間預留的內存沒法知足程序須要,就會出現一次「Concurrent Mode Failure」失敗,這時虛擬機將啓動後備預案:
臨時啓用Serial Old收集器來從新進行老年代的垃圾收集,這樣停頓時間就很長了。
因此說參數-XX:CMSInitiatingOccupancyFraction(超過此值激活CMS收集器)
設置得過高很容易致使大量「Concurrent Mode Failure」失敗,性能反而下降。
CMS收集器提供了一個-XX:+UseCMSCompactAtFullCollection開關參數(默認就是開啓的),
用於在CMS收集器頂不住要進行FullGC時開啓內存碎片的合併整理過程,內存整理的過程是沒法併發的,
空間碎片問題沒有了,但停頓時間不得不變長。
虛擬機設計者還提供了另一個參數-XX:CMSFullGCsBeforeCompaction,
這個參數是用於設置執行多少次不壓縮的Full GC後,跟着來一次帶壓縮的(默認值爲0,表示每次進入Full GC時都進行碎片整理)。
G1收集器
G1是一款面向服務端應用的垃圾收集器。
HotSpot開發團隊賦予它的使命是將來能夠替換掉JDK 1.5中發佈的CMS收集器。
並行與併發:
G1能充分利用多CPU、多核環境下的硬件優點,使用多個CPU(CPU或者CPU核心)來縮短Stop-The-World停頓的時間,
部分其餘收集器本來須要停頓Java線程執行的GC動做,G1收集器仍然能夠經過併發的方式讓Java程序繼續執行。
分代收集:
與其餘收集器同樣,分代概念在G1中依然得以保留。
雖然G1能夠不須要其餘收集器配合就能獨立管理整個GC堆,
但它可以採用不一樣的方式去處理新建立的對象和已經存活了一段時間、熬過屢次GC的舊對象以獲取更好的收集效果。
空間整合:
與CMS的「標記—清理」算法不一樣,G1從總體來看是基於「標記—整理」算法實現的收集器,
從局部(兩個Region之間)上來看是基於「複製」算法實現的,
但不管如何,這兩種算法都意味着G1運做期間不會產生內存空間碎片,收集後能提供規整的可用內存。
這種特性有利於程序長時間運行,分配大對象時不會由於沒法找到連續內存空間而提早觸發下一次GC。
可預測的停頓:
這是G1相對於CMS的另外一大優點,下降停頓時間是G1和CMS共同的關注點,
但G1除了追求低停頓外,還能創建可預測的停頓時間模型,
能讓使用者明確指定在一個長度爲M毫秒的時間片斷內,消耗在垃圾收集上的時間不得超過N毫秒,。
在G1以前的其餘收集器進行收集的範圍都是整個新生代或者老年代,而G1再也不是這樣。
使用G1收集器時,Java堆的內存佈局就與其餘收集器有很大差異,它將整個Java堆劃分爲多個大小相等的獨立區域(Region),
雖然還保留有新生代和老年代的概念,但新生代和老年代再也不是物理隔離的了,它們都是一部分Region(不須要連續)的集合。
觸發full gc的狀況
(1)System.gc()方法的調用。
此方法的調用是建議JVM進行Full GC,雖然只是建議而非必定,但不少狀況下它會觸發Full GC,
從而增長Full GC的頻率,也即增長了間歇性停頓的次數。
強烈影響系建議能不使用此方法就別使用,讓虛擬機本身去管理它的內存,
可經過經過 -XX:+DisableExplicitGC 來禁止RMI(Java遠程方法調用)調用System.gc。
(2)年老代空間不足。
年老代空間只有在新生代對象轉入及建立爲大對象、大數組時纔會出現不足的現象,
當執行Full GC後空間仍然不足,則拋出錯誤:java.lang.OutOfMemoryError: Java heap space。
爲避免以上兩種情況引發的Full GC,
調優時應儘可能作到讓對象在Minor GC(新生代GC)階段被回收、讓對象在新生代多存活一段時間及不要建立過大的對象及數組。
(3)Permanet Generation空間滿了。
Permanet Generation中存放的爲一些class的信息等,當系統中要加載的類、反射的類和調用的方法較多時,
Permanet Generation可能會被佔滿,在未配置爲採用CMS GC的狀況下會執行Full GC。
若是通過Full GC仍然回收不了,那麼JVM會拋出錯誤信息:java.lang.OutOfMemoryError: PermGen space 。
爲避免Perm Gen佔滿形成Full GC現象,可採用的方法爲增大Perm Gen空間或轉爲使用CMS GC。
(4)經過Minor GC(新生代GC)後進入老年代的平均大小大於老年代的可用內存。
若是發現統計數聽說以前Minor GC的平均晉升大小比目前old gen剩餘的空間大,
則不會觸發Minor GC(新生代GC)而是轉爲觸發full GC。
(5)由Eden區、From Space區向To Space區複製時,對象大小大於To Space可用內存,
則把該對象轉存到老年代,且老年代的可用內存小於該對象大小。
內存泄漏
年老代堆空間被佔滿
異常: java.lang.OutOfMemoryError: Java heap space
解決方案:
代碼內的內存泄漏能夠經過一些分析工具進行分析,而後找出泄漏點進行改善。
Survivor Space區域內存不夠致使大量對象進如年老代,經過優化代碼和增長Survivor Space等方式去優化。
持久代被佔滿(大量動態反射生成的類不斷被加載)
異常:java.lang.OutOfMemoryError: PermGen space
解決方案:
增長持久代的空間 -XX:MaxPermSize=100M。
若是有自定義類加載的須要排查下本身的代碼問題。
堆棧溢出
異常:java.lang.StackOverflowError
通常就是遞歸沒返回,或者循環調用形成
線程堆棧滿
異常:Fatal: Stack size too small
java中一個線程的空間大小是有限制的。JDK5.0之後這個值是1M。與這個線程相關的數據將會保存在其中。
可是當線程空間滿了之後,將會出現上面異常。
解決:
增長線程棧大小。-Xss2m。但這個配置沒法解決根本問題,還要看代碼部分是否有形成泄漏的部分。
系統內存被佔滿
異常:java.lang.OutOfMemoryError: unable to create new native thread
這個異常是因爲操做系統沒有足夠的資源來產生這個線程形成的。
系統建立線程時,除了要在Java堆中分配內存外,操做系統自己也須要分配資源來建立線程。
解決:
1. 從新設計系統減小線程數量。
2. 線程數量不能減小的狀況下,經過-Xss減少單個線程大小。以便能生產更多的線程。
優化GC步驟
首先須要觀察目前垃圾回收的狀況,分析出老年代和年輕代回收的狀況,適當的去調整內存大小和-XX:SurvivorRatio的比例。
根據垃圾收集器的特性,選擇適合本身業務的垃圾收集器,通常來講如今的WEB服務都是CMS+ParNew收集器。
根據CMS收集器通常來講就會產生大量碎片,根據本身的需求懸着相應的壓縮頻率便可。
不斷的調整jvm內存比例,老年代、年輕代、以及持久代的比例,直到測試出一個比較滿意的值。
JVM參數配置
http://www.javashuo.com/article/p-waizdunp-u.htmlhtml