吞吐量是指GC的時(shí)間與運(yùn)行總時(shí)間的比值,比如系統(tǒng)運(yùn)行了100分鐘,而GC占用了一分鐘,那么吞吐量就是99%,吞吐量優(yōu)先一般運(yùn)用于對響應(yīng)性要求不高的場合,比如web應(yīng)用,因?yàn)榫W(wǎng)絡(luò)傳輸本來就有延遲的問題,GC造成的短暫的暫停使得用戶以為是網(wǎng)絡(luò)阻塞所致。
吞吐量優(yōu)先可以通過-XX:GCTimeRatio來指定。
當(dāng)通過-XX:GCTimeRatio不能滿足系統(tǒng)的要求以后,我們可以更加細(xì)致的來對JVM進(jìn)行調(diào)優(yōu)。
首先因?yàn)橐蟾咄掏铝?,這樣就需要一個(gè)較大的Young generation,此時(shí)就需要引入“Parallel scavenging Collector”,可以通過參數(shù):-XX:UseParallelGC來配置。
java -server -Xms3072m -Xmx3072m -XX:NewSize=2560m -XX:MaxNewSize=2560 XX:SurvivorRatio=2 -XX:+UseParallelGC
當(dāng)年輕代使用了"Parallel scavenge collector"后,老生代就不能使用"CMS"GC了,在JDK1.6之前,此時(shí)老生代只能采用串行收集,而JDK1.6引入了并行版本的老生代收集器,可以用參數(shù)-XX:UseParallelOldGC來配置。
缺省情況下,Parallel scavenging Collector 會開啟與cpu數(shù)量相同的線程進(jìn)行并行的收集,但是也可以調(diào)節(jié)并行的線程數(shù)。假如你想用4個(gè)并行的線程去收集Young generation的話,那么就可以配置-XX:ParallelGCThreads=4,此時(shí)JVM的配置參數(shù)如下:
java -server -Xms3072m -Xmx3072m -XX:NewSize=2560m -XX:MaxNewSize=2560 XX:SurvivorRatio=2 -XX:+UseParallelGC -XX:ParallelGCThreads=4
在采用了"Parallel scavenge collector"后,此GC會根據(jù)運(yùn)行時(shí)的情況自動(dòng)調(diào)節(jié)survivor ratio來使得性能最優(yōu),因此"Parallel scavenge collector"應(yīng)該總是開啟此參數(shù)。
此時(shí)JVM的參數(shù)配置如下:
java -server -Xms3072m -Xmx3072m -XX:+UseParallelGC -XX:ParallelGCThreads=4 -XX:+UseAdaptiveSizePolicy
響應(yīng)時(shí)間優(yōu)先是指GC每次運(yùn)行的時(shí)間不能太久,這種情況一般使用與對及時(shí)性要求很高的系統(tǒng),比如股票系統(tǒng)等。
響應(yīng)時(shí)間優(yōu)先可以通過參數(shù)-XX:MaxGCPauseMillis來配置,配置以后JVM將會自動(dòng)調(diào)節(jié)年輕代,老生代的內(nèi)存分配來滿足參數(shù)設(shè)置。
在一般情況下,JVM的默認(rèn)配置就可以滿足要求,只有默認(rèn)配置不能滿足系統(tǒng)的要求時(shí)候,才會根據(jù)具體的情況來對JVM進(jìn)行性能調(diào)優(yōu)。如果采用默認(rèn)的配置不能滿足系統(tǒng)的要求,那么此時(shí)就可以自己動(dòng)手來調(diào)節(jié)。
此時(shí)"Young generation"可以采用"Parallel copying collector",而"Old generation"則可以采用"Concurrent Collector",
舉個(gè)例子來說,以下參數(shù)設(shè)置了新生代用Parallel Copying Collector,老生代采用CMS收集器。
java -server -Xms512m -Xmx512m -XX:NewSize=64m -XX:MaxNewSize=64m -XX:SurvivorRatio=2 -XX:+UseConcMarkSweepGC -XX:+UseParNewGC
此時(shí)需要注意兩個(gè)問題:
1 如果沒有指定-XX:+UseParNewGC,則采用默認(rèn)的非并行版本的copy collector.
2 如果在一個(gè)單CPU的系統(tǒng)上設(shè)置了-XX:+UseParNewGC ,則默認(rèn)還是采用缺省的copy collector.
默認(rèn)情況下,Parallel copy collector啟動(dòng)和CPU數(shù)量一樣的線程,也可以通過參數(shù)-XX:ParallelGCThreads來指定,比如你想用3個(gè)線程去進(jìn)行并發(fā)的復(fù)制收集,那么可以改變上述參數(shù)如下:
java -server -Xms512m -Xmx512m -XX:NewSize=64m -XX:MaxNewSize=64m -XX:SurvivorRatio=2 -XX:ParallelGCThreads=4 -XX:+UseConcMarkSweepGC -XX:+UseParNewGC
默認(rèn)情況下,CMS gc在"old generation"空間占用率高于68%的時(shí)候,就會進(jìn)行垃圾收集,而如果想控制收集的臨界值,可以通過參數(shù):-XX:CMSInitiatingOccupancyFraction來控制,比如改變上述的JVM配置如下:
java -server -Xms512m -Xmx512m -XX:NewSize=64m -XX:MaxNewSize=64m -XX:SurvivorRatio=2 -XX:ParallelGCThreads=4 -XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:CMSInitiatingOccupancyFraction=35
Minor GC主要負(fù)責(zé)收集Young Generation,Minor GC一般在新生代不夠用的情況下觸發(fā),比如我們一次性創(chuàng)建了很多對象等。
List<byte[]> buffer = new ArrayList<byte[]>(); for(int i=0;i<8*1024;i++){
buffer.add(new byte[1024]);
}
以上代碼通過一個(gè)字節(jié)數(shù)組的List模擬觸發(fā)Minor gc,設(shè)置JVM參數(shù)如下:
-verbose:gc -Xmn10M -Xms64M -Xmx64M -XX:+PrintGC
設(shè)置以上參數(shù)以后,因?yàn)?span style="FONT-FAMILY: Times New Roman">-Xmn=10M,默認(rèn)-XX:SurvivorRatio=8 ,則eden的空間大小為8M,當(dāng)eden對象大小超過8M的時(shí)候就會觸發(fā)Minor gc.
運(yùn)行的結(jié)果如下:
[GC 8192K->8030K(64512K), 0.0243391 secs]
從運(yùn)行結(jié)果可以看出,gc前和gc后的eden區(qū)的占用情況,需要注意的是括號里(64512)這個(gè)數(shù)值時(shí)63M,它不包括一塊Survivor 空間。
這里需要注意的一點(diǎn)就是,如果創(chuàng)建的對象大于eden的大小,那么將不會通過Survivor空間復(fù)制,直接轉(zhuǎn)移到old generation.
調(diào)整以上代碼如下:
List<byte[]> buffer = new ArrayList<byte[]>();
buffer.add(new byte[8*1024*1024]);
通過同樣的JVM參數(shù)運(yùn)行,則發(fā)現(xiàn)不會觸發(fā)Minor gc,這是因?yàn)閷ο蟪^了eden的大小,從而直接分配到了Old generation.
Old generation 空間滿是因?yàn)?/span>Young generation提升到Old generation的對象+Old generation的本來的大小已經(jīng)接近或者超過了Old generation的大小。對于CMS GC,當(dāng)Old generation空間使用率接近某一個(gè)比例,可以通過參數(shù)-XX:CMS InitialingOccupancyFraction,此參數(shù)表示Old generation的使用率,默認(rèn)為68%。
Young generation對象提升到Old generation對象有以下三種情況:
Ø 分配的對象大于eden空間的大小
Ø 在Young generation代中經(jīng)過了-XX:MaxTenuringThreshold次復(fù)制任然存活的對象
Ø Minor gc的時(shí)候,放不進(jìn)to survivor的對象
當(dāng)Major GC以后,如果還沒有足夠的空間可以用的話,此時(shí)就會拋出java.lang.OutOfMemory:java heap space,當(dāng)出現(xiàn)此錯(cuò)誤的時(shí)候,說明可能存在內(nèi)存泄露現(xiàn)象的,這時(shí)候就需要我們對程序進(jìn)行檢查看看什么地方存在內(nèi)存泄露的。
我們可以通過以下代碼來模擬一下java.lang.OutOfMemory:java heap space的發(fā)生:
List<byte[]> buffer = new ArrayList<byte[]>();
buffer.add(new byte[10*1024*1024]);
以上代碼分配了一個(gè)10M的字節(jié)數(shù)組,我們通過以下的參數(shù)運(yùn)行:
-verbose:gc -Xmn10M -Xms20M -Xmx20M -XX:+PrintGC
以上參數(shù)指定Young generation的空間大小為10M,Old generation空間大小為10M。
運(yùn)行結(jié)果如下:
[GC 327K->134K(19456K), 0.0056516 secs]
[Full GC 134K->134K(19456K), 0.0178891 secs]
[Full GC 134K->131K(19456K), 0.0141412 secs]
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at Test.main(Test.java:30)
從運(yùn)行結(jié)果可以看出,JVM進(jìn)行了一次Minor gc和兩次的Major gc,從Major gc的輸出可以看出,gc以后old區(qū)使用率為134K,而字節(jié)數(shù)組為10M,加起來大于了old generation的空間,所以拋出了異常,如果調(diào)整-Xms21M,-Xmx21M,那么就不會觸發(fā)gc操作也不會出現(xiàn)異常了。
Perm Generation空間主要存放Class對象,Field,Method對象,當(dāng)一次性加載太多的類或者在熱部署以后不卸載類的情況(比如在Jboss服務(wù)器中,如果經(jīng)常熱部署一些應(yīng)用就會出現(xiàn)Perm 空間溢出)就會造成Perm Generation被占滿,此時(shí)就會出現(xiàn):
java.lang.OutOfMemory:PermGen space,在出現(xiàn)此異常的時(shí)候,如果是因?yàn)闊岵渴鹨鸬模覀冎匦聠?dòng)AS就可以了,如果是因?yàn)榧虞d的類太多,此時(shí)可以通過-XX:PermSize和-XX:MaxPermSize調(diào)整。
java.lang.StackOverflowError錯(cuò)誤表示JVM棧溢出,出現(xiàn)這個(gè)錯(cuò)誤的原因一般都是遞歸的層次太深,或者無限的遞歸造成的。出現(xiàn)這種錯(cuò)誤的時(shí)候首先要對應(yīng)用程序進(jìn)行檢查,看看是那些代碼造成了棧溢出,如果是遞歸造成的可以改為迭代方式實(shí)現(xiàn)。
JVM同樣也提供了一個(gè)參數(shù)來讓我們調(diào)節(jié)運(yùn)行時(shí)棧空間的大小。-XX:Xss=256K表示棧空間最大為256K.我們也可以調(diào)大,但是建議不要對此參數(shù)進(jìn)行調(diào)節(jié)。
java.lang.OutOfMemoryError: Java heap space這個(gè)錯(cuò)誤表示JVM的新生代和老生代的內(nèi)存不足。出現(xiàn)這個(gè)錯(cuò)誤說明應(yīng)用程序出現(xiàn)了內(nèi)存溢出或者程序所需要的內(nèi)存大于JVM的內(nèi)存設(shè)置了。
遇到這個(gè)問題的時(shí)候,首先我們可以調(diào)節(jié)JVM的Heap內(nèi)存的大小,具體可以通過-Xmx -Xms來進(jìn)行設(shè)置,如果設(shè)置大以后還是會出現(xiàn)內(nèi)存溢出,那么說明應(yīng)用程序本身存在內(nèi)存泄露,這個(gè)時(shí)候就需要我們對應(yīng)用程序進(jìn)行檢查,找出導(dǎo)致內(nèi)存泄露的地方,然后修正。
java.lang.OutOfMemory:PermGen space錯(cuò)誤是由Perm space空間不足。一般出現(xiàn)這個(gè)錯(cuò)誤是由加載了太多的類或者大量使用了動(dòng)態(tài)代理造成的。如果出現(xiàn)了這個(gè)錯(cuò)誤,我們可以將Perm空間調(diào)大一點(diǎn)。
-XX:PermSize=16M -XX:MaxPermSize=64M
參考資料
1 http://developers.sun.com/mobility/midp/articles/garbage/
2 http://developers.sun.com/mobility/midp/articles/garbagecollection2/
3 http://blogs.sun.com/watt/resource/jvm-options-list.html
4 http://java.sun.com/developer/technicalArticles/Programming/turbo/
5 http://www.ibm.com/developerworks/library/j-jtp10283/index.html?S_TACT=105AGX52&S_CMP=cn-a-j
6 http://www.ibm.com/developerworks/library/j-jtp11253/index.html?S_TACT=105AGX52&S_CMP=cn-a-j