1. 程式人生 > >深入JVM系列(二)之GC機制、收集器與GC調優

深入JVM系列(二)之GC機制、收集器與GC調優

一、回顧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):

[java] view plaincopyprint?
  1. -XX:-PrintGCPrintmessagesatgarbagecollection.Manageable.    
  2. -XX:-PrintGCDetailsPrintmoredetailsatgarbagecollection.Manageable.(Introducedin1.4.0.)    
  3. -XX:-PrintGCTimeStampsPrinttimestampsatgarbagecollection.Manageable(Introducedin1.4.0.)   
-XX:-PrintGCPrintmessagesatgarbagecollection.Manageable.  
-XX:-PrintGCDetailsPrintmoredetailsatgarbagecollection.Manageable.(Introducedin1.4.0.)  
-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的官方說明: 

[plain] view plaincopyprint?
  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. 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.  
  3. 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. 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. 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.

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查詢存活物件。第二次用來,詳細檢查存活物件。整個過程如下:

[java] view plaincopyprint?
  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)(復原)  
* stop all application threads; do the initial mark; resume all application threads(第一次暫停,初始話標記)
* do the concurrent mark (uses one procesor for the concurrent work)(執行是標記)
* do the concurrent pre-clean (uses one processor for the concurrent work)(準備清理)
* stop all application threads; do the remark; resume all application threads(第二次暫停,標記,檢查)
* do the concurrent sweep (uses one processor for the concurrent work)(執行過程中清理)
* do the concurrent reset (uses one processor for the concurrent work)(復原)


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

[java] view plaincopyprint?
  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 提供一個指導收集數  
-XX:+CMSIncrementalMode default: disabled 啟動i-CMS模式(must with -XX:+UseConcMarkSweepGC)
-XX:+CMSIncrementalPacing default: disabled 提供自動校正功能
-XX:CMSIncrementalDutyCycle=<N> default: 50 啟動CMS的上線
-XX:CMSIncrementalDutyCycleMin=<N> default: 10 啟動CMS的下線
-XX:CMSIncrementalSafetyFactor=<N> default: 10 用來計算迴圈次數
-XX:CMSIncrementalOffset=<N> default: 0 最小迴圈次數(This is the percentage (0-100) by which the incremental mode duty cycle is shifted to the right within the period between minor collections.)
-XX:CMSExpAvgFactor=<N> default: 25 提供一個指導收集數


     SUN推薦的使用引數是: [java] view plaincopyprint?
  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  
-XX:+UseConcMarkSweepGC \
-XX:+CMSIncrementalMode \
-XX:+CMSIncrementalPacing \
-XX:CMSIncrementalDutyCycleMin=0 \
-XX:CMSIncrementalDutyCycle=10 \
-XX:+PrintGC Details \
-XX:+PrintGCTimeStamps \
-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

[java] view plaincopyprint?
  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
Garbage First(G1)
jdk1.6 update 14 or jdk7
few flags need to set
-XX:MaxGCPauseMillis=100
-XX:GCPauseIntervalMillis=6000


     還沒嘗試過使用…

Summary

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


}

用以下兩種引數執行,會執行幾次YGC幾次FGC? -Xms30M -Xmx30M -Xmn10M  -Xloggc:gc.log -XX:+PrintTenuringDistribution -XX:+UseParallelGC
[java] view plaincopyprint?
  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]  
2.062: [GC
Desired survivor size 1310720 bytes, new threshold 7 (max 15)
 6467K->6312K(29440K), 0.0038214 secs]
4.066: [GC
Desired survivor size 1310720 bytes, new threshold 7 (max 15)
 12536K->12440K(29440K), 0.0036804 secs]
6.070: [GC
Desired survivor size 1310720 bytes, new threshold 7 (max 15)
 18637K->18584K(29440K), 0.0040175 secs]
6.074: [Full GC 18584K->18570K(29440K), 0.0031329 secs]
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
[java] view plaincopyprint?
  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]  
2.047: [GC
Desired survivor size 524288 bytes, new threshold 15 (max 15)
- age   1:     142024 bytes,     142024 total
 6472K->6282K(29696K), 0.0048686 secs]
4.053: [GC
Desired survivor size 524288 bytes, new threshold 15 (max 15)
- age   2:     141880 bytes,     141880 total
 12512K->12426K(29696K), 0.0047334 secs]
6.058: [GC
Desired survivor size 524288 bytes, new threshold 15 (max 15)
- age   3:     141880 bytes,     141880 total
 18627K->18570K(29696K), 0.0049135 secs]
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/目錄下執行下面的命令。

[java] view plaincopyprint?
  1. java -jar -Xmn4m -Xms16m -Xmx16m SwingSet2.jar  
java -jar -Xmn4m -Xms16m -Xmx16m SwingSet2.jar


系統輸出為:
[java] view plaincopyprint?
  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  
Exception in thread "Image Fetcher 0" java.lang.OutOfMemoryError: Java heap space
Exception in thread "Image Fetcher 3" java.lang.OutOfMemoryError: Java heap space
Exception in thread "Image Fetcher 1" java.lang.OutOfMemoryError: Java heap space
Exception in thread "Image Fetcher 2" java.lang.OutOfMemoryError: Java heap space



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

將上面的命令換成以下命令執行則應用