JVM的GC機制、收集器與GC調優

一、回顧JVM內存分配


需要了解更多內存模式與內存分配的,請看 深入JVM系列(一)之內存模型與內存分配


1.1、內存分配:

1、對象優先在EDEN分配
2、大對象直接進入老年代 
3、長期存活的對象將進入老年代 
4、適齡對象也可能進入老年代:動態對象年齡判斷



動態對象年齡判斷:

虛擬機並不總是要求對象的年齡必須達到MaxTenuringThreshold才能晉升到老年代,當Survivor空間的相同年齡的所有對象大小總和大於Survivor空間的一半,年齡大於或等於該年齡的對象就可以直接進入老年代,無需等到MaxTenuringThreshold中指定的年齡



1.2、總結一下:


1、對象優先在Eden分配,這裏大部分對象具有朝生夕滅的特徵,Minor GC主要清理該處
2、大對象(佔內存大)、老對象(使用頻繁)

3、Survivor無法容納的對象,將進入老年代,Full GC的主要清理該處


二、JVM的GC機制


JVM有2個GC線程


第一個線程負責回收Heap的Young區
第二個線程在Heap不足時,遍歷Heap,將Young 區升級爲Older區


Older區的大小等於-Xmx減去-Xmn,不能將-Xms的值設的過大,因爲第二個線程被迫運行會降低JVM的性能


2.1、堆內存GC


       JVM(採用分代回收的策略),用較高的頻率對年輕的對象(young generation)進行YGC,而對老對象(tenured generation)較少(tenured generation 滿了後才進行)進行Full GC。這樣就不需要每次GC都將內存中所有對象都檢查一遍。


2.2、非堆內存不GC


      GC不會在主程序運行期對PermGen Space進行清理,所以如果你的應用中有很多CLASS(特別是動態生成類,當然permgen space存放的內容不僅限於類)的話,就很可能出現PermGen Space錯誤。


2.3、內存申請、對象衰老過程


2.3.1、內存申請過程


  1. JVM會試圖爲相關Java對象在Eden中初始化一塊內存區域;
  2. 當Eden空間足夠時,內存申請結束。否則到下一步;
  3. JVM試圖釋放在Eden中所有不活躍的對象(minor collection),釋放後若Eden空間仍然不足以放入新對象,則試圖將部分Eden中活躍對象放入Survivor區;
  4. Survivor區被用來作爲Eden及old的中間交換區域,當OLD區空間足夠時,Survivor區的對象會被移到Old區,否則會被保留在Survivor區;
  5. 當old區空間不夠時,JVM會在old區進行major collection;
  6. 完全垃圾收集後,若Survivor及old區仍然無法存放從Eden複製過來的部分對象,導致JVM無法在Eden區爲新對象創建內存區域,則出現"Out of memory錯誤";

2.3.2、對象衰老過程


  1. 新創建的對象的內存都分配自eden。Minor collection的過程就是將eden和在用survivor space中的活對象copy到空閒survivor space中。對象在young generation裏經歷了一定次數(可以通過參數配置)的minor collection後,就會被移到old generation中,稱爲tenuring。

GC觸發條件

GC類型 觸發條件 觸發時發生了什麼 注意 查看方式
YGC eden空間不足

清空Eden+from survivor中所有no ref的對象佔用的內存
將eden+from sur中所有存活的對象copy到to sur中
一些對象將晉升到old中:
    to sur放不下的
    存活次數超過turning threshold中的
重新計算tenuring threshold(serial parallel GC會觸發此項)

重新調整Eden 和from的大小(parallel GC會觸發此項)

全過程暫停應用
是否爲多線程處理由具體的GC決定
jstat –gcutil 
gc log
FGC old空間不足
perm空間不足
顯示調用System.GC, RMI等的定時觸發
YGC時的悲觀策略
dump live的內存信息時(jmap –dump:live)
清空heap中no ref的對象
permgen中已經被卸載的classloader中加載的class信息

如配置了CollectGenOFirst,則先觸發YGC(針對serial GC)
如配置了ScavengeBeforeFullGC,則先觸發YGC(針對serial GC)
全過程暫停應用
是否爲多線程處理由具體的GC決定

是否壓縮需要看配置的具體GC
jstat –gcutil 
gc log



permanent generation空間不足會引發Full GC,仍然不夠會引發PermGen Space錯誤。


三、GC監視、收集器與GC調優


3.1、監視JVM GC


首先說一下如何監視JVM GC,可以用JDK中的jstat工具,也可以在java程序啓動的opt里加上如下幾個參數(注:這兩個參數只針對SUN的HotSpotVM):


  1. -XX:-PrintGCPrintmessagesatgarbagecollection.Manageable.    
  2. -XX:-PrintGCDetailsPrintmoredetailsatgarbagecollection.Manageable.(Introducedin1.4.0.)    
  3. -XX:-PrintGCTimeStampsPrinttimestampsatgarbagecollection.Manageable(Introducedin1.4.0.)   
  1. -XX:-PrintGCPrintmessagesatgarbagecollection.Manageable.    
  2. -XX:-PrintGCDetailsPrintmoredetailsatgarbagecollection.Manageable.(Introducedin1.4.0.)    
  3. -XX:-PrintGCTimeStampsPrinttimestampsatgarbagecollection.Manageable(Introducedin1.4.0.)   


當把-XX:-PrintGCDetails加入到javaopt裏以後可以看見如下輸出:


[GC[DefNew:34538K->2311K(36352K),0.0232439secs]45898K->15874K(520320K),0.0233874secs]
[FullGC[Tenured:13563K->15402K(483968K),0.2368177secs]21163K->15402K(520320K),[Perm:28671K->28635K(28672K)],0.2371537secs]


他們分別顯示了JVM GC的過程,清理出了多少空間。第一行GC使用的是‘普通GC’(MinorCollections),第二行使用的是‘全GC’(MajorCollections)。他們的區別很大,在第一行最後我們可以看見他的時間是0.0233874秒,而第二行的FullGC的時間是0.2371537秒。第二行的時間是第一行的接近10倍,也就是我們這次調優的重點,減少FullGC的次數,以爲FullGC會暫停程序比較長的時間,如果FullGC的次數比較多。程序就會經常性的假死。

注:

GC信息的格式


[GC [<collector>: <starting occupancy1> -> <ending occupancy1>, <pause time1> secs] <starting occupancy3> -> <ending occupancy3>, <pause time3> secs]
<collector> GC爲minor收集過程中使用的垃圾收集器起的內部名稱.
<starting occupancy1> young generation 在進行垃圾收集前被對象使用的存儲空間.
<ending occupancy1> young generation 在進行垃圾收集後被對象使用的存儲空間
<pause time1> minor收集使應用暫停的時間長短(秒) 
<starting occupancy3> 整個堆(Heap Size)在進行垃圾收集前被對象使用的存儲空間
<ending occupancy3> 整個堆(Heap Size)在進行垃圾收集後被對象使用的存儲空間
<pause time3> 整個垃圾收集使應用暫停的時間長短(秒),包括major收集使應用暫停的時間(如果發生了major收集).

GC信息的選項


-XX:+PrintGCDetails 顯示GC的詳細信息
-XX:+PrintGCApplicationConcurrentTime 打印應用執行的時間
-XX:+PrintGCApplicationStoppedTime 打印應用被暫停的時間



3.2、collector收集器的種類   

   

GC在 HotSpot VM 5.0裏有四種:

incremental (sometimes called train) low pause collector已被廢棄,不在介紹.

類別 serial collector parallel collector
throughput collector )
concurrent collector
(concurrent low pause collector)
介紹 單線程收集器
使用單線程去完成所有的gc工作,沒有線程間的通信,這種方式會相對高效
並行收集器
使用多線程的方式,利用多CUP來提高GC的效率
主要以到達一定的吞吐量爲目標
併發收集器
使用多線程的方式,利用多CUP來提高GC的效率
併發完成大部分工作,使得gc pause短
試用場景 單處理器機器且沒有pause time的要求 適用於科學技術和後臺處理
有中規模/大規模數據集大小的應用且運行在多處理器上,關注吞吐量(throughput)
適合中規模/大規模數據集大小的應用,應用服務器,電信領域
關注response time,而不是throughput
使用 Client模式下默認
可使用
可用-XX:+UseSerialGC強制使用
優點:對server應用沒什麼優點
缺點:慢,不能充分發揮硬件資源

Server模式下默認

--YGC:PS FGC:Parallel MSC

可用-XX:+UseParallelGC或-XX:+UseParallelOldGC強制指定

--ParallelGC代表FGC爲Parallel MSC

--ParallelOldGC代表FGC爲Parallel Compacting

優點:高效

缺點:當heap變大後,造成的暫停時間會變得比較長

可用-XX:+UseConcMarkSweepGC強制指定
優點:
對old進行回收時,對應用造成的暫停時間非常端,適合對latency要求比較高的應用
缺點:
1.內存碎片和浮動垃圾
2.old去的內存分配效率低
3.回收的整個耗時比較長
4.和應用爭搶CPU
內存回收觸發 YGC
eden空間不足
FGC
old空間不足
perm空間不足
顯示調用System.gc() ,包括RMI等的定時觸發
YGC時的悲觀策略
dump live的內存信息時(jmap –dump:live)
YGC
eden空間不足
FGC
old空間不足
perm空間不足
顯示調用System.gc() ,包括RMI等的定時觸發
YGC時的悲觀策略--YGC前&YGC後
dump live的內存信息時(jmap –dump:live)
YGC
eden空間不足
CMS GC
1.old Gen的使用率大的一定的比率 默認爲92%
2.配置了CMSClassUnloadingEnabled,且Perm Gen的使用達到一定的比率 默認爲92%
3.Hotspot自己根據估計決定是否要觸法
4.在配置了ExplictGCInvokesConcurrent的情況下顯示調用了System.gc.
Full GC(Serial MSC)
promotion failed 或 concurrent Mode Failure時;
內存回收觸發時發生了什麼 YGC
eden空間不足
FGC
old空間不足
perm空間不足
顯示調用System.gc() ,包括RMI等的定時觸發
YGC時的悲觀策略
dump live的內存信息時(jmap –dump:live)
YGC
同serial動作基本相同,不同點:
1.多線程處理
2.YGC的最後不僅重新計算Tenuring Threshold,還會重新調整Eden和From的大小
FGC
1.如配置了ScavengeBeforeFullGC(默認),則先觸發YGC(??)
2.MSC:清空heap中的no ref對象,permgen中已經被卸載的classloader中加載的class信息,並進行壓縮
3.Compacting:清空heap中部分no ref的對象,permgen中已經被卸載的classloader中加載的class信息,並進行部分壓縮
多線程做以上動作.
YGC
同serial動作基本相同,不同點:
1.多線程處理
CMSGC:
1.old gen到達比率時只清除old gen中no ref的對象所佔用的空間
2.perm gen到達比率時只清除已被清除的classloader加載的class信息
FGC
同serial
細節參數 可用-XX:+UseSerialGC強制使用
-XX:SurvivorRatio=x,控制eden/s0/s1的大小
-XX:MaxTenuringThreshold,用於控制對象在新生代存活的最大次數
-XX:PretenureSizeThreshold=x,控制超過多大的字節的對象就在old分配.
-XX:SurvivorRatio=x,控制eden/s0/s1的大小
-XX:MaxTenuringThreshold,用於控制對象在新生代存活的最大次數

-XX:UseAdaptiveSizePolicy 去掉YGC後動態調整eden from已經tenuringthreshold的動作

-XX:ParallelGCThreads 設置並行的線程數

-XX:CMSInitiatingOccupancyFraction 設置old gen使用到達多少比率時觸發
-XX:CMSInitiatingPermOccupancyFraction,設置Perm Gen使用到達多少比率時觸發
-XX:+UseCMSInitiatingOccupancyOnly禁止hostspot自行觸發CMS GC

注:

  • throughput collector與concurrent low pause collector的區別是throughput collector只在young area使用使用多線程,而concurrent low pause collector則在tenured generation也使用多線程。
  • 根據官方文檔,他們倆個需要在多CPU的情況下,才能發揮作用。在一個CPU的情況下,會不如默認的serial collector,因爲線程管理需要耗費CPU資源。而在兩個CPU的情況下,也提高不大。只是在更多CPU的情況下,纔會有所提高。當然 concurrent low pause collector有一種模式可以在CPU較少的機器上,提供儘可能少的停頓的模式,見CMS GC Incremental mode
  • 當要使用throughput collector時,在java opt里加上-XX:+UseParallelGC,啓動throughput collector收集。也可加上-XX:ParallelGCThreads=<desired number>來改變線程數。還有兩個參數 -XX:MaxGCPauseMillis=<nnn>和 -XX:GCTimeRatio=<nnn>,MaxGCPauseMillis=<nnn>用來控制最大暫停時間,而-XX: GCTimeRatio可以提高GC說佔CPU的比,以最大話的減小heap。

附註SUN的官方說明: 

  1. 1. The throughput collector: this collector uses a parallel version of the young generation collector. It is used if the -XX:+UseParallelGC option is passed on the command line. The tenured generation collector is the same as the serial collector.  
  2.   
  3. 2. The concurrent low pause collector: this collector is used if the -Xincgc™ or -XX:+UseConcMarkSweepGC is passed on the command line. The concurrent collector is used to collect the tenured generation and does most of the collection concurrently with the execution of the application. The application is paused for short periods during the collection. A parallel version of the young generation copying collector is used with the concurrent collector. The concurrent low pause collector is used if the option -XX:+UseConcMarkSweepGC is passed on the command line.  
  4.   
  5. 3. The incremental (sometimes called train) low pause collector: this collector is used only if -XX:+UseTrainGC is passed on the command line. This collector has not changed since the J2SE Platform version 1.4.2 and is currently not under active development. It will not be supported in future releases. Please see the 1.4.2 GC Tuning Document for information on this collector.  
  1. 1. The throughput collector: this collector uses a parallel version of the young generation collector. It is used if the -XX:+UseParallelGC option is passed on the command line. The tenured generation collector is the same as the serial collector.  
  2.   
  3. 2. The concurrent low pause collector: this collector is used if the -Xincgc™ or -XX:+UseConcMarkSweepGC is passed on the command line. The concurrent collector is used to collect the tenured generation and does most of the collection concurrently with the execution of the application. The application is paused for short periods during the collection. A parallel version of the young generation copying collector is used with the concurrent collector. The concurrent low pause collector is used if the option -XX:+UseConcMarkSweepGC is passed on the command line.  
  4.   
  5. 3. The incremental (sometimes called train) low pause collector: this collector is used only if -XX:+UseTrainGC is passed on the command line. This collector has not changed since the J2SE Platform version 1.4.2 and is currently not under active development. It will not be supported in future releases. Please see the 1.4.2 GC Tuning Document for information on this collector.  


CMS GC Incremental mode


       當要使用 concurrent low pause collector時,在java的opt里加上 -XX:+UseConcMarkSweepGC。concurrent low pause collector還有一種爲CPU少的機器準備的模式,叫Incremental mode。這種模式使用一個CPU來在程序運行的過程中GC,只用很少的時間暫停程序,檢查對象存活。

       在Incremental mode裏,每個收集過程中,會暫停兩次,第二次略長。第一次用來,簡單從root查詢存活對象。第二次用來,詳細檢查存活對象。整個過程如下:

  1. * stop all application threads; do the initial mark; resume all application threads(第一次暫停,初始話標記)  
  2. do the concurrent mark (uses one procesor for the concurrent work)(運行是標記)  
  3. do the concurrent pre-clean (uses one processor for the concurrent work)(準備清理)  
  4. * stop all application threads; do the remark; resume all application threads(第二次暫停,標記,檢查)  
  5. do the concurrent sweep (uses one processor for the concurrent work)(運行過程中清理)  
  6. do the concurrent reset (uses one processor for the concurrent work)(復原)  
  1. * stop all application threads; do the initial mark; resume all application threads(第一次暫停,初始話標記)  
  2. do the concurrent mark (uses one procesor for the concurrent work)(運行是標記)  
  3. do the concurrent pre-clean (uses one processor for the concurrent work)(準備清理)  
  4. * stop all application threads; do the remark; resume all application threads(第二次暫停,標記,檢查)  
  5. do the concurrent sweep (uses one processor for the concurrent work)(運行過程中清理)  
  6. do the concurrent reset (uses one processor for the concurrent work)(復原)  


       當要使用Incremental mode時,需要使用以下幾個變量:

  1. -XX:+CMSIncrementalMode default: disabled 啓動i-CMS模式(must with -XX:+UseConcMarkSweepGC)  
  2. -XX:+CMSIncrementalPacing default: disabled 提供自動校正功能  
  3. -XX:CMSIncrementalDutyCycle=<N> default50 啓動CMS的上線  
  4. -XX:CMSIncrementalDutyCycleMin=<N> default10 啓動CMS的下線  
  5. -XX:CMSIncrementalSafetyFactor=<N> default10 用來計算循環次數  
  6. -XX:CMSIncrementalOffset=<N> default0 最小循環次數(This is the percentage (0-100) by which the incremental mode duty cycle is shifted to the right within the period between minor collections.)  
  7. -XX:CMSExpAvgFactor=<N> default25 提供一個指導收集數  
  1. -XX:+CMSIncrementalMode default: disabled 啓動i-CMS模式(must with -XX:+UseConcMarkSweepGC)  
  2. -XX:+CMSIncrementalPacing default: disabled 提供自動校正功能  
  3. -XX:CMSIncrementalDutyCycle=<N> default50 啓動CMS的上線  
  4. -XX:CMSIncrementalDutyCycleMin=<N> default10 啓動CMS的下線  
  5. -XX:CMSIncrementalSafetyFactor=<N> default10 用來計算循環次數  
  6. -XX:CMSIncrementalOffset=<N> default0 最小循環次數(This is the percentage (0-100) by which the incremental mode duty cycle is shifted to the right within the period between minor collections.)  
  7. -XX:CMSExpAvgFactor=<N> default25 提供一個指導收集數  



     SUN推薦的使用參數是:
  1. -XX:+UseConcMarkSweepGC \  
  2. -XX:+CMSIncrementalMode \  
  3. -XX:+CMSIncrementalPacing \  
  4. -XX:CMSIncrementalDutyCycleMin=0 \  
  5. -XX:CMSIncrementalDutyCycle=10 \  
  6. -XX:+PrintGC Details \  
  7. -XX:+PrintGCTimeStamps \  
  8. -XX:-TraceClassUnloading  
  1. -XX:+UseConcMarkSweepGC \  
  2. -XX:+CMSIncrementalMode \  
  3. -XX:+CMSIncrementalPacing \  
  4. -XX:CMSIncrementalDutyCycleMin=0 \  
  5. -XX:CMSIncrementalDutyCycle=10 \  
  6. -XX:+PrintGC Details \  
  7. -XX:+PrintGCTimeStamps \  
  8. -XX:-TraceClassUnloading  


注:如果使用throughput collector和concurrent low pause collector,這兩種垃圾收集器,需要適當的挺高內存大小,以爲多線程做準備。

3.3、如何選擇collector


  • app運行在單處理器機器上且沒有pause time的要求,讓vm選擇UseSerialGC.
  • 重點考慮peak application performance(高性能),沒有pause time太嚴格要求,讓vm選擇或者UseParallelGC+UseParallelOldGC(optionally).
  • 重點考慮response time,pause time要小UseConcMarkSweepGC.

Garbage Collctor – Future

  1. Garbage First(G1)  
  2. jdk1.6 update 14 or jdk7  
  3. few flags need to set  
  4. -XX:MaxGCPauseMillis=100  
  5. -XX:GCPauseIntervalMillis=6000  
  1. Garbage First(G1)  
  2. jdk1.6 update 14 or jdk7  
  3. few flags need to set  
  4. -XX:MaxGCPauseMillis=100  
  5. -XX:GCPauseIntervalMillis=6000  


     還沒嘗試過使用…

Summary

  1. import java.util.ArrayList;  
  2. import java.util.List;  
  3. public class SummaryCase {  
  4.     public static void main(String[] args) throws InterruptedException {  
  5.         List<Object> caches = new ArrayList<Object>();  
  6.         for (int i = 0; i < 7; i++) {  
  7.             caches.add(new byte[1024 * 1024 * 3]);  
  8.             Thread.sleep(1000);  
  9.         }  
  10.         caches.clear();  
  11.         for (int i = 0; i < 2; i++) {  
  12.             caches.add(new byte[1024 * 1024 * 3]);  
  13.             Thread.sleep(1000);  
  14.         }  
  15.     }  
  16. }  
  1. import java.util.ArrayList;  
  2. import java.util.List;  
  3. public class SummaryCase {  
  4.     public static void main(String[] args) throws InterruptedException {  
  5.         List<Object> caches = new ArrayList<Object>();  
  6.         for (int i = 0; i < 7; i++) {  
  7.             caches.add(new byte[1024 * 1024 * 3]);  
  8.             Thread.sleep(1000);  
  9.         }  
  10.         caches.clear();  
  11.         for (int i = 0; i < 2; i++) {  
  12.             caches.add(new byte[1024 * 1024 * 3]);  
  13.             Thread.sleep(1000);  
  14.         }  
  15.     }  
  16. }  

}


用以下兩種參數執行,會執行幾次YGC幾次FGC?

-Xms30M -Xmx30M -Xmn10M  -Xloggc:gc.log -XX:+PrintTenuringDistribution -XX:+UseParallelGC

  1. 2.062: [GC  
  2. Desired survivor size 1310720 bytes, new threshold 7 (max 15)  
  3.  6467K->6312K(29440K), 0.0038214 secs]  
  4. 4.066: [GC  
  5. Desired survivor size 1310720 bytes, new threshold 7 (max 15)  
  6.  12536K->12440K(29440K), 0.0036804 secs]  
  7. 6.070: [GC  
  8. Desired survivor size 1310720 bytes, new threshold 7 (max 15)  
  9.  18637K->18584K(29440K), 0.0040175 secs]  
  10. 6.074: [Full GC 18584K->18570K(29440K), 0.0031329 secs]  
  11. 8.078: [Full GC 24749K->3210K(29440K), 0.0045590 secs]  
  1. 2.062: [GC  
  2. Desired survivor size 1310720 bytes, new threshold 7 (max 15)  
  3.  6467K->6312K(29440K), 0.0038214 secs]  
  4. 4.066: [GC  
  5. Desired survivor size 1310720 bytes, new threshold 7 (max 15)  
  6.  12536K->12440K(29440K), 0.0036804 secs]  
  7. 6.070: [GC  
  8. Desired survivor size 1310720 bytes, new threshold 7 (max 15)  
  9.  18637K->18584K(29440K), 0.0040175 secs]  
  10. 6.074: [Full GC 18584K->18570K(29440K), 0.0031329 secs]  
  11. 8.078: [Full GC 24749K->3210K(29440K), 0.0045590 secs]  

(具體分析見http://rdc.taobao.com/team/jm/archives/440)

-Xms30M -Xmx30M -Xmn10M  -Xloggc:gc.log -XX:+PrintTenuringDistribution -XX:+UseSerialGC
  1. 2.047: [GC  
  2. Desired survivor size 524288 bytes, new threshold 15 (max 15)  
  3. - age   1:     142024 bytes,     142024 total  
  4.  6472K->6282K(29696K), 0.0048686 secs]  
  5. 4.053: [GC  
  6. Desired survivor size 524288 bytes, new threshold 15 (max 15)  
  7. - age   2:     141880 bytes,     141880 total  
  8.  12512K->12426K(29696K), 0.0047334 secs]  
  9. 6.058: [GC  
  10. Desired survivor size 524288 bytes, new threshold 15 (max 15)  
  11. - age   3:     141880 bytes,     141880 total  
  12.  18627K->18570K(29696K), 0.0049135 secs]  
  13. 8.063: [Full GC 24752K->3210K(29696K), 0.0077895 secs]  
  1. 2.047: [GC  
  2. Desired survivor size 524288 bytes, new threshold 15 (max 15)  
  3. - age   1:     142024 bytes,     142024 total  
  4.  6472K->6282K(29696K), 0.0048686 secs]  
  5. 4.053: [GC  
  6. Desired survivor size 524288 bytes, new threshold 15 (max 15)  
  7. - age   2:     141880 bytes,     141880 total  
  8.  12512K->12426K(29696K), 0.0047334 secs]  
  9. 6.058: [GC  
  10. Desired survivor size 524288 bytes, new threshold 15 (max 15)  
  11. - age   3:     141880 bytes,     141880 total  
  12.  18627K->18570K(29696K), 0.0049135 secs]  
  13. 8.063: [Full GC 24752K->3210K(29696K), 0.0077895 secs]  
(具體分析見http://rdc.taobao.com/team/jm/archives/458)


四、GC調優的小例子


例1:Heap size 設置 


JVM 堆的設置是指java程序運行過程中JVM可以調配使用的內存空間的設置.JVM在啓動的時候會自動設置Heap size的值,其初始空間(即-Xms)是物理內存的1/64,最大空間(-Xmx)是物理內存的1/4。可以利用JVM提供的-Xmn -Xms -Xmx等選項可進行設置。


Heap size 的大小是Young Generation 和Tenured Generaion 之和。
當在JAVA_HOME下demo/jfc/SwingSet2/目錄下執行下面的命令。


  1. java -jar -Xmn4m -Xms16m -Xmx16m SwingSet2.jar  
  1. java -jar -Xmn4m -Xms16m -Xmx16m SwingSet2.jar  


系統輸出爲:
  1. Exception in thread "Image Fetcher 0" java.lang.OutOfMemoryError: Java heap space  
  2. Exception in thread "Image Fetcher 3" java.lang.OutOfMemoryError: Java heap space  
  3. Exception in thread "Image Fetcher 1" java.lang.OutOfMemoryError: Java heap space  
  4. Exception in thread "Image Fetcher 2" java.lang.OutOfMemoryError: Java heap space  
  1. Exception in thread "Image Fetcher 0" java.lang.OutOfMemoryError: Java heap space  
  2. Exception in thread "Image Fetcher 3" java.lang.OutOfMemoryError: Java heap space  
  3. Exception in thread "Image Fetcher 1" java.lang.OutOfMemoryError: Java heap space  
  4. Exception in thread "Image Fetcher 2" java.lang.OutOfMemoryError: Java heap space  



除了這些異常信息外,還會發現程序的響應速度變慢了。這說明Heap size 設置偏小,GC佔用了更多的時間,而應用分配到的執行時間較少。
提示:在JVM中如果98%的時間是用於GC且可用的Heap size 不足2%的時候將拋出此異常信息。 


將上面的命令換成以下命令執行則應用能夠正常使用,且未拋出任何異常。
java -jar -Xmn4m -Xms16m -Xmx32m SwingSet2.jar


提示:Heap Size 最大不要超過可用物理內存的80%,一般的要將-Xms和-Xmx選項設置爲相同,而-Xmn爲1/4的-Xmx值。


例2:Young Generation(-Xmn)的設置 


在本例中看一下Young Generation的設置不同將有什麼現象發生。
假設將Young generation 的大小設置爲4M ,即執行java -jar -verbose:gc -Xmn4m -Xms32m -Xmx32m -XX:+PrintGCDetails
 SwingSet2.jar,


屏幕輸出如下(節選)

  1. [GC [DefNew: 3968K->64K(4032K), 0.0923407 secs] 3968K->2025K(32704K), 0.0931870 secs]  
  2. [GC [DefNew: 4021K->64K(4032K), 0.0356847 secs] 5983K->2347K(32704K), 0.0365441 secs]  
  3. [GC [DefNew: 3995K->39K(4032K), 0.0090603 secs] 6279K->2372K(32704K), 0.0093377 secs]  
  1. [GC [DefNew: 3968K->64K(4032K), 0.0923407 secs] 3968K->2025K(32704K), 0.0931870 secs]  
  2. [GC [DefNew: 4021K->64K(4032K), 0.0356847 secs] 5983K->2347K(32704K), 0.0365441 secs]  
  3. [GC [DefNew: 3995K->39K(4032K), 0.0090603 secs] 6279K->2372K(32704K), 0.0093377 secs]  


將程序體制並將Young Generation的大小設置爲8M,即執行

  1. java -jar -verbose:gc -Xmn8m -Xms32m -Xmx32m -XX:+PrintGCDetails SwingSet2.jar  
  1. java -jar -verbose:gc -Xmn8m -Xms32m -Xmx32m -XX:+PrintGCDetails SwingSet2.jar  


屏幕輸出如下(節選)

  1. [GC [DefNew: 7808K->192K(8000K), 0.1016784 secs] 7808K->2357K(32576K), 0.1022834 secs]  
  2. [GC [DefNew: 8000K->70K(8000K), 0.0149659 secs] 10165K->2413K(32576K), 0.0152557 secs]  
  3. [GC [DefNew: 7853K->59K(8000K), 0.0069122 secs] 10196K->2403K(32576K), 0.0071843 secs]  
  4. [GC [DefNew: 7867K->171K(8000K), 0.0075745 secs] 10211K->2681K(32576K), 0.0078376 secs]  
  5. [GC [DefNew: 7970K->192K(8000K), 0.0201353 secs] 10480K->2923K(32576K), 0.0206867 secs]  
  6. [GC [DefNew: 7979K->30K(8000K), 0.1787079 secs] 10735K->4824K(32576K), 0.1790065 secs]  
  1. [GC [DefNew: 7808K->192K(8000K), 0.1016784 secs] 7808K->2357K(32576K), 0.1022834 secs]  
  2. [GC [DefNew: 8000K->70K(8000K), 0.0149659 secs] 10165K->2413K(32576K), 0.0152557 secs]  
  3. [GC [DefNew: 7853K->59K(8000K), 0.0069122 secs] 10196K->2403K(32576K), 0.0071843 secs]  
  4. [GC [DefNew: 7867K->171K(8000K), 0.0075745 secs] 10211K->2681K(32576K), 0.0078376 secs]  
  5. [GC [DefNew: 7970K->192K(8000K), 0.0201353 secs] 10480K->2923K(32576K), 0.0206867 secs]  
  6. [GC [DefNew: 7979K->30K(8000K), 0.1787079 secs] 10735K->4824K(32576K), 0.1790065 secs]  


那麼根據GC輸出的信息(這裏取第一行)做一下Minor收集的比較。可以看出兩次的Minor收集分別在Young generation中找回3904K(3968K->64K)和7616K(7808K->192K)而對於整個jvm則找回 1943K(3968K->2025)和5451K(7808K->2357K)。第一種情況下Minor收集了大約50%(1943/3904)的對象,而另外的50%的對象則被移到了tenured generation。在第二中情況下Minor收集了大約72%的對象,只有不到30%的對象被移到了Tenured Generation.這個例子說明此應用在的Young generation 設置爲4m時顯的偏小。
提示:一般的Young Generation的大小是整個Heap size的1/4。Young generation的minor收集率應一般在70%以上。當然在實際的應用中需要根據具體情況進行調整。


例3:Young Generation對應用響應的影響 


還是使用-Xmn4m 和-Xmn8m進行比較,先執行下面的命令

  1. java -jar -verbose:gc -Xmn4m -Xms32m -Xmx32m -XX:+PrintGCDetails -XX:+PrintGCApplicationConcurrentTime -XX:+PrintGCApplicationStoppedTime SwingSet2.jar  
  1. java -jar -verbose:gc -Xmn4m -Xms32m -Xmx32m -XX:+PrintGCDetails -XX:+PrintGCApplicationConcurrentTime -XX:+PrintGCApplicationStoppedTime SwingSet2.jar  


屏幕輸出如下(節選)
  1. Application time: 0.5114944 seconds  
  2. [GC [DefNew: 3968K->64K(4032K), 0.0823952 secs] 3968K->2023K(32704K), 0.0827626 secs]  
  3. Total time for which application threads were stopped: 0.0839428 seconds  
  4. Application time: 0.9871271 seconds  
  5. [GC [DefNew: 4020K->64K(4032K), 0.0412448 secs] 5979K->2374K(32704K), 0.0415248 secs]  
  6. Total time for which application threads were stopped: 0.0464380 seconds  
  1. Application time: 0.5114944 seconds  
  2. [GC [DefNew: 3968K->64K(4032K), 0.0823952 secs] 3968K->2023K(32704K), 0.0827626 secs]  
  3. Total time for which application threads were stopped: 0.0839428 seconds  
  4. Application time: 0.9871271 seconds  
  5. [GC [DefNew: 4020K->64K(4032K), 0.0412448 secs] 5979K->2374K(32704K), 0.0415248 secs]  
  6. Total time for which application threads were stopped: 0.0464380 seconds  


Young Generation 的Minor收集佔用的時間可以計算如下

應用線程被中斷的總時常/(應用執行總時?L+應用線程被中斷的總時常),那麼在本例中垃圾收集佔用的時?L約爲系統的5%~14%。那麼當垃圾收集佔用的時間的比例越大的時候,系統的響應將越慢。


提示:對於互聯網應用系統的響應稍微慢一些,用戶是可以接受的,但是對於GUI類型的應用響應速度慢將會給用戶帶來非常不好的體驗。


例4:如何決定Tenured Generation 的大小 


分別以-Xmn8m -Xmx32m和-Xmn8m -Xmx64m進行對比,先執行

  1. java -verbose:gc -Xmn8m -Xmx32m-XX:+PririntGCDetails -XX:+PrintGCTimeStamps java類  
  1. java -verbose:gc -Xmn8m -Xmx32m-XX:+PririntGCDetails -XX:+PrintGCTimeStamps java類  


命令行將提示(只提取了Major收集)

  1. 111.042: [GC 111.042: [DefNew: 8128K->8128K(8128K), 0.0000505 secs]111.042: [Tenured: 18154K->2311K(24576K), 0.1290354 secs] 26282K->2311K(32704K), 0.1293306 secs]  
  2. 122.463: [GC 122.463: [DefNew: 8128K->8128K(8128K), 0.0000560 secs]122.463: [Tenured: 18630K->2366K(24576K), 0.1322560 secs] 26758K->2366K(32704K), 0.1325284 secs]  
  3. 133.896: [GC 133.897: [DefNew: 8128K->8128K(8128K), 0.0000443 secs]133.897: [Tenured: 18240K->2573K(24576K), 0.1340199 secs] 26368K->2573K(32704K), 0.1343218 secs]  
  4. 144.112: [GC 144.112: [DefNew: 8128K->8128K(8128K), 0.0000544 secs]144.112: [Tenured: 16564K->2304K(24576K), 0.1246831 secs] 24692K->2304K(32704K), 0.1249602 secs]  
  1. 111.042: [GC 111.042: [DefNew: 8128K->8128K(8128K), 0.0000505 secs]111.042: [Tenured: 18154K->2311K(24576K), 0.1290354 secs] 26282K->2311K(32704K), 0.1293306 secs]  
  2. 122.463: [GC 122.463: [DefNew: 8128K->8128K(8128K), 0.0000560 secs]122.463: [Tenured: 18630K->2366K(24576K), 0.1322560 secs] 26758K->2366K(32704K), 0.1325284 secs]  
  3. 133.896: [GC 133.897: [DefNew: 8128K->8128K(8128K), 0.0000443 secs]133.897: [Tenured: 18240K->2573K(24576K), 0.1340199 secs] 26368K->2573K(32704K), 0.1343218 secs]  
  4. 144.112: [GC 144.112: [DefNew: 8128K->8128K(8128K), 0.0000544 secs]144.112: [Tenured: 16564K->2304K(24576K), 0.1246831 secs] 24692K->2304K(32704K), 0.1249602 secs]  


再執行

  1. java -verbose:gc -Xmn8m -Xmx64m-XX:+PririntGCDetails -XX:+PrintGCTimeStamps java類  
  1. java -verbose:gc -Xmn8m -Xmx64m-XX:+PririntGCDetails -XX:+PrintGCTimeStamps java類  


命令行將提示(只提取了Major收集)

  1. 90.597: [GC 90.597: [DefNew: 8128K->8128K(8128K), 0.0000542 secs]90.597: [Tenured: 49841K->5141K(57344K), 0.2129882 secs] 57969K->5141K(65472K), 0.2133274 secs]  
  2. 120.899: [GC 120.899: [DefNew: 8128K->8128K(8128K), 0.0000550 secs]120.899: [Tenured: 50384K->2430K(57344K), 0.2216590 secs] 58512K->2430K(65472K), 0.2219384 secs]  
  3. 153.968: [GC 153.968: [DefNew: 8128K->8128K(8128K), 0.0000511 secs]153.968: [Tenured: 51164K->2309K(57344K), 0.2193906 secs] 59292K->2309K(65472K), 0.2196372 secs]  
  1. 90.597: [GC 90.597: [DefNew: 8128K->8128K(8128K), 0.0000542 secs]90.597: [Tenured: 49841K->5141K(57344K), 0.2129882 secs] 57969K->5141K(65472K), 0.2133274 secs]  
  2. 120.899: [GC 120.899: [DefNew: 8128K->8128K(8128K), 0.0000550 secs]120.899: [Tenured: 50384K->2430K(57344K), 0.2216590 secs] 58512K->2430K(65472K), 0.2219384 secs]  
  3. 153.968: [GC 153.968: [DefNew: 8128K->8128K(8128K), 0.0000511 secs]153.968: [Tenured: 51164K->2309K(57344K), 0.2193906 secs] 59292K->2309K(65472K), 0.2196372 secs]  


可以看出在Heap size 爲32m的時候系統等候時間約爲0.13秒左右,而設置爲64m的時候等候時間則增大到0.22秒左右了。但是在32m的時候系統的Major收集間隔爲 10秒左右,而Heap size 增加到64m的時候爲30秒。那麼應用在運行的時候是選擇32m還是64m呢?如果應用是web類型(即要求有大的吞吐量)的應用則使用64m(即 heapsize大一些)的比較好。對於要求實時響應要求較高的場合(例如GUI型的應用)則使用32m比較好一些。 
注意:
1。因爲在JVM5運行時已經對Heap-size進行了優化,所以在能確定java應用運行時不會超過默認的Heap size的情況下建議不要對這些值進行修改。
2。 Heap size的 -Xms -Xmn 設置不要超出物理內存的大小。否則會提示“Error occurred during initialization of VM Could not reserve enough space for object heap”。


例5:如何縮短minor收集的時間 


下面比較一下采用-XX:+UseParNewGC選項和不採用它的時候的minor收集將有什麼不同。先執行
  1. java -jar -server -verbose:gc -Xmn8m -Xms32m -Xmx32m SwingSet2.jar   
  1. java -jar -server -verbose:gc -Xmn8m -Xms32m -Xmx32m SwingSet2.jar   


系統將輸出如下信息(片段〕
  1. [GC 7807K->2641K(32576K), 0.0676654 secs]  
  2. [GC 10436K->3108K(32576K), 0.0245328 secs]  
  3. [GC 10913K->3176K(32576K), 0.0072865 secs]  
  4. [GC 10905K->4097K(32576K), 0.0223928 secs]  
  1. [GC 7807K->2641K(32576K), 0.0676654 secs]  
  2. [GC 10436K->3108K(32576K), 0.0245328 secs]  
  3. [GC 10913K->3176K(32576K), 0.0072865 secs]  
  4. [GC 10905K->4097K(32576K), 0.0223928 secs]  


之後再執行 

  1. java -jar -server -verbose:gc -XX:+UseParNewGC -Xmn8m -Xms32m -Xmx32m SwingSet2.jar  
  1. java -jar -server -verbose:gc -XX:+UseParNewGC -Xmn8m -Xms32m -Xmx32m SwingSet2.jar  


系統將輸出如下信息(片段〕
  1. [ParNew 7808K->2656K(32576K), 0.0447687 secs]  
  2. [ParNew 10441K->3143K(32576K), 0.0179422 secs]  
  3. [ParNew 10951K->3177K(32576K), 0.0031914 secs]  
  4. [ParNew 10985K->3867K(32576K), 0.0154991 secs]  
  1. [ParNew 7808K->2656K(32576K), 0.0447687 secs]  
  2. [ParNew 10441K->3143K(32576K), 0.0179422 secs]  
  3. [ParNew 10951K->3177K(32576K), 0.0031914 secs]  
  4. [ParNew 10985K->3867K(32576K), 0.0154991 secs]  


很顯然使用了-XX:+UseParNewGC選項的minor收集的時間要比不使用的時候優。


例6:如何縮短major收集的時間 


下面比較一下采用-XX:+UseConcMarkSweepGC選項和不採用它的時候的major收集將有什麼不同。先執行
  1. java -jar -verbose:gc -XX:+UseConcMarkSweepGC -XX:+UseParNewGC -Xmn64m -Xms256m -Xmx256m SwingSet2.jar  
  1. java -jar -verbose:gc -XX:+UseConcMarkSweepGC -XX:+UseParNewGC -Xmn64m -Xms256m -Xmx256m SwingSet2.jar  


系統將輸出如下信息(片段〕
  1. [Full GC 22972K->18690K(262080K), 0.2326676 secs]  
  2. [Full GC 18690K->18690K(262080K), 0.1701866 secs  
  1. [Full GC 22972K->18690K(262080K), 0.2326676 secs]  
  2. [Full GC 18690K->18690K(262080K), 0.1701866 secs  


之後再執行 

  1. java -jar -verbose:gc -XX:+UseParNewGC -Xmn64m -Xms256m -Xmx256m SwingSet2.jar  
  1. java -jar -verbose:gc -XX:+UseParNewGC -Xmn64m -Xms256m -Xmx256m SwingSet2.jar  


系統將輸出如下信息(片段〕
  1. [Full GC 56048K->18869K(260224K), 0.3104852 secs]  
  1. [Full GC 56048K->18869K(260224K), 0.3104852 secs]  


提示:此選項在Heap Size 比較大而且Major收集時間較長的情況下使用更合適。


例7:關於-server選項


 在JVM中將運行中的類認定爲server-class的時候使用此選項。SUN 的Hot Spot JVM5 如果判斷到系統的配置滿足如下條件則自動將運行的類認定爲server-class,並且會自動設置jvm的選項(當沒有手工設置這選項的時候〕而且 HOTSPOT JVM5提供了自動調優的功能,他會根據JVM的運行情況進行調整。如果沒有特別的需要是不需要太多的人工干預的。SUN形象的稱這個機制爲“人體工學 ”(Ergonomics〕。具體可以參考http://java.sun.com/docs/hotspot/gc5.0/ergo5.html
*.具有2個或更多個物理的處理器
*.具有2G或者更多的物理內存
提示:此選項要放在所有選項的前面。例如:java -server 其他選項 java類

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章