JVM GC是JVM的內存回收算法,調整JVM GC(Garbage Collection),能夠極大的減小因爲GC工做,而致使的程序運行中斷方面的問題,進而適當的提升Java程序的工做效率。可是調整GC是以個極爲複雜的過程,因此咱們要了解JVM內存組成,回收算法,對象分配機制。java
Java堆由Perm區和Heap區組成,Heap區由Old區和New區(也叫Young區)組成,New區由Eden區、From區和To區(Survivor)組成。算法
Eden區用於存放新生成的對象。Eden中的對象生命不會超過一次Minor GC。數組
Survivor Space 有兩個,存放每次垃圾回收後存活的對象,即圖的S0和S1。服務器
Old Generation Old區,也稱老生代,主要存放應用程序中生命週期長的存活對象多線程
JVM初始分配的內存由-Xms指定,JVM最大分配的內存由-Xmx指定。默認空餘堆內存小於40%時,JVM就會增大堆直到-Xmx的最大限制;空餘堆內存大於70%時,JVM會減小堆直到 -Xms的最小限制。所以服務器通常設置-Xms、-Xmx相等以免在每次GC 後調整堆的大小。併發
-XX:NewRatio= 參數能夠設置Young與Old的大小比例,-server時默認爲1:2,若是過小,會使大對象直接分配到old區去,增大major collections的執行的次數,影響性能。
-XX:SurvivorRatio= 參數能夠設置Eden與Survivor的比例,默認爲1:8,Survivio大了會浪費,若是小了的話,會使一些大對象在作minor gc時,直接從eden區潛逃到old區,讓old區的gc頻繁。這個參數保持默認就行了,通常狀況下,對性能影響不大。jvm
一、 串行GC(Serial Copying)性能
client模式下的默認GC方式,也可以使用-XX:+UseSerialGC指定。spa
二、 並行回收GC(Parallel Scavenge)
server模式下的默認GC方式,也可用-XX:+UseParallelGC強制指定。線程
採用PS時,默認狀況下JVM會在運行時動態調整Eden:S0:S1的比例,若是不但願自動調整可使用-XX:-UseAdaptiveSizePolicy參數,內存分配和回收的算法和串行相同,惟一不一樣僅在於回收時爲多線程。
三、 並行GC(ParNew)
CMS GC時默認採用,也能夠採用-XX:+UseParNewGC指定。內存分配、回收和PS相同,不一樣的僅在於會收拾會配合CMS作些處理。
Old區的幾種Collector
一、 串行GC(Serial MSC)
client模式下的默認GC方式,可經過-XX:+UseSerialGC強制指定。每次進行所有回收,進行Compact,很是耗費時間。
二、 並行GC(Parallel MSC)(備註,吞吐量大,可是gc的時候響應很慢)
server模式下的默認GC方式,也可用-XX:+UseParallelGC=強制指定。能夠在選項後加等號來制定並行的線程數。
三、 併發GC(CMS)線上環境採用的GC方式,也就是Realese環境的方式。(備註,響應比並行gc快不少,可是犧牲了必定的吞吐量)
使用CMS是爲了減小GC執行時的停頓時間,垃圾回收線程和應用線程同時執行,可使用-XX:+UseConcMarkSweepGC=指定使用,後邊接等號指定併發線程數。CMS每次回收只停頓很短的時間,分別在開始的時候(Initial Marking),和中間(Final Marking)的時候,第二次時間略長。具體CMS的過程能夠參考相關文檔。JStat中將Initial Mark和Remark都統計成了FGC。
CMS一個比較大的問題是碎片和浮動垃圾問題(Floating Gabage)。碎片是因爲CMS默認不對內存進行Compact所致,能夠經過-XX:+UseCMSCompactAtFullCollection。
整體來說,Old區的大小較大,垃圾回收算法較費時間,致使較長時間的應用線程中止工做,並且須要進行Compact,因此不該該出現較多Major GC。Major GC的時間經常是Minor GC的幾十倍。JVM內存調優的重點,減小Major GC 的次數,由於爲Major GC 會暫停程序比較長的時間,若是Major GC 的次數比較多,意味着應用程序的JVM內存參數須要進行調整。
1. 對象優先在Eden分配
若是Eden區不足分配對象,會作一個minor gc,回收內存,嘗試分配對象,若是依然不足分配,才分配到Old區。
2.大對象直接進入老年代
大對象是指須要大量連續內存空間的Java對象,最典型的大對象就是那種很長的字符串及數組,虛擬機提供了一個-XX:PretenureSizeThreshold參數,令大於這個設置值的對象直接在老年代中分配。這樣作的目的是避免在Eden區及兩個Survivor區之間發生大量的內存拷貝(新生代採用複製算法收集內存)。PretenureSizeThreshold參數只對Serial和ParNew兩款收集器有效,
3.長期存活的對象將進入老年代
在經歷了屢次的Minor GC後仍然存活:在觸發了Minor GC後,存活對象被存入Survivor區在經歷了屢次Minor GC以後,若是仍然存活的話,則該對象被晉升到Old區。
虛擬機既然採用了分代收集的思想來管理內存,那內存回收時就必須能識別哪些對象應當放在新生代,哪些對象應放在老年代中。爲了作到這點,虛擬機給每一個對象定義了一個對象年齡(Age)計數器。若是對象在Eden出生並通過第一次Minor GC後仍然存活,而且能被Survivor容納的話,將被移動到Survivor空間中,並將對象年齡設爲1。對象在Survivor區中每熬過一次Minor GC,年齡就增長1歲,當它的年齡增長到必定程度(默認爲15歲)時,就會被晉升到老年代中。對象晉升老年代的年齡閾值,能夠經過參數-XX:MaxTenuringThreshold來設置。
4.動態對象年齡斷定
爲了能更好地適應不一樣程序的內存情況,虛擬機並不老是要求對象的年齡必須達到MaxTenuringThreshold才能晉升老年代,若是在Survivor空間中相同年齡全部對象大小的總和大於Survivor空間的一半,年齡大於或等於該年齡的對象就能夠直接進入老年代,無須等到MaxTenuringThreshold中要求的年齡。
5.Minor GC後Survivor空間不足就直接放入Old區
6.空間分配擔保
在發生Minor GC時,虛擬機會檢測以前每次晉升到老年代的平均大小是否大於老年代的剩餘空間大小,若是大於,則改成直接進行一次Full GC。若是小於,則查看HandlePromotionFailure設置是否容許擔保失敗;若是容許,那隻會進行Minor GC;若是不容許,則也要改成進行一次Full GC。大部分狀況下都仍是會將HandlePromotionFailure開關打開,避免Full GC過於頻繁。
1.概覽監視gc。
jmap -heap [pid] 查看內存分佈
jstat -gcutil [pid] 1000 每隔1s輸出java進程的gc狀況
2.詳細監視gc。
在jvm啓動參數,加入-verbose:gc -XX:+PrintGCTimeStamps -XX:+PrintGCDetails -Xloggc:./gc.log。
輸入示例:
[GC [ParNew: 11450951K->1014116K(11673600K), 0.8698830 secs] 27569972K->17943420K(37614976K), 0.8699520 secs] [Times: user=11.28 sys=0.82, real=0.86 secs]
表示發生一次minor GC,ParNew是新生代的gc算法,11450951K表示eden區的存活對象的內存總和,1014116K表示回收後的存活對象的內存總和,11673600K是整個eden區的內存總和。0.8699520 secs表示minor gc花費的時間。
27569972K表示整個heap區的存活對象總和,17943420K表示回收後整個heap區的存活對象總和,37614976K表示整個heap區的內存總和。
[Full GC [Tenured: 27569972K->16569972K(27569972K), 180.2368177 secs] 36614976K->27569972K(37614976K), [Perm : 28671K->28635K(28672K)], 0.2371537 secs]
表示發生了一次Full GC,整個JVM都停頓了180多秒,輸出說明同上。只是Tenured: 27569972K->16569972K(27569972K)表示的是old區,而上面是eden區。
更多能夠參考 阿里分享的ppt sunjdk1.6gc.pptx