轉(zhuǎn)載blog.csdn.net/ning109314/article/details/10411495/
JVM工作原理和特點主要是指操作系統(tǒng)裝入JVM是通過jdk中Java.exe來完成,通過下面4步來完成JVM環(huán)境.
1.創(chuàng)建JVM裝載環(huán)境和配置
2.裝載JVM.dll
3.初始化JVM.dll并掛界到JNIENV(JNI調(diào)用接口)實例
4.調(diào)用JNIEnv實例裝載并處理class類残腌。
在我們運行和調(diào)試Java程序的時候,經(jīng)常會提到一個JVM的概念.JVM是Java程序運行的環(huán)境,但是他同時一個操作系統(tǒng)的一個應(yīng)用程序一個進程,因此他也有他自己的運行的生命周期,也有自己的代碼和數(shù)據(jù)空間.
首先來說一下JVM工作原理中的jdk這個東西,不管你是初學(xué)者還是高手,是j2ee程序員還是j2se程序員,jdk總是在幫我們做一些事情.我們在了解Java之前首先大師們會給我們提供說jdk這個東西.它在Java整個體系中充當著什么角色呢?我很驚嘆sun大師們設(shè)計天才,能把一個如此完整的體系結(jié)構(gòu)化的如此完美.jdk在這個體系中充當一個生產(chǎn)加工中心,產(chǎn)生所有的數(shù)據(jù)輸出,是所有指令和戰(zhàn)略的執(zhí)行中心.本身它提供了Java的完整方案,可以開發(fā)目前Java能支持的所有應(yīng)用和系統(tǒng)程序.這里說一個問題,大家會問,那為什么還有j2me,j2ee這些東西,這兩個東西目的很簡單,分別用來簡化各自領(lǐng)域內(nèi)的開發(fā)和構(gòu)建過程.jdk除了JVM之外,還有一些核心的API,集成API,用戶工具,開發(fā)技術(shù),開發(fā)工具和API等組成
好了,廢話說了那么多,來點于主題相關(guān)的東西吧.JVM在整個jdk中處于最底層,負責于操作系統(tǒng)的交互,用來屏蔽操作系統(tǒng)環(huán)境,提供一個完整的Java運行環(huán)境,因此也就虛擬計算機. 操作系統(tǒng)裝入JVM是通過jdk中Java.exe來完成,通過下面4步來完成JVM環(huán)境.
1.創(chuàng)建JVM裝載環(huán)境和配置
2.裝載JVM.dll
3.初始化JVM.dll并掛界到JNIENV(JNI調(diào)用接口)實例
4.調(diào)用JNIEnv實例裝載并處理class類诚卸。
一.JVM裝入環(huán)境烘贴,JVM提供的方式是操作系統(tǒng)的動態(tài)連接文件.既然是文件那就一個裝入路徑的問題物喷,Java是怎么找這個路徑的呢?當你在調(diào)用Javatest的時候煤辨,操作系統(tǒng)會在path下在你的Java.exe程序苍糠,Java.exe就通過下面一個過程來確定JVM的路徑和相關(guān)的參數(shù)配置了.下面基于Windows的實現(xiàn)的分析.
首先查找jre路徑雅镊,Java是通過GetApplicationHome api來獲得當前的Java.exe絕對路徑,c:\j2sdk1.4.2_09\bin\Java.exe,那么它會截取到絕對路徑c:\j2sdk1.4.2_09\捺信,判斷c:\j2sdk1.4.2_09\bin\Java.dll文件是否存在酌媒,如果存在就把c:\j2sdk1.4.2_09\作為jre路徑,如果不存在則判斷c:\j2sdk1.4.2_09\jre\bin\Java.dll是否存在迄靠,如果存在這c:\j2sdk1.4.2_09\jre作為jre路徑.如果不存在調(diào)用GetPublicJREHome查HKEY_LOCAL_MACHINE\Software\JavaSoft\Java Runtime Environment\“當前JRE版本號”\JavaHome的路徑為jre路徑秒咨。
然后裝載JVM.cfg文件JRE路徑+\lib+\ARCH(CPU構(gòu)架)+\JVM.cfgARCH(CPU構(gòu)架)的判斷是通過Java_md.c中GetArch函數(shù)判斷的,該函數(shù)中windows平臺只有兩種情況:WIN64的‘ia64’掌挚,其他情況都為‘i386’雨席。以我的為例:C:\j2sdk1.4.2_09\jre\lib\i386\JVM.cfg.主要的內(nèi)容如下:
-client?KNOWN
-server?KNOWN
-hotspot?ALIASED_TO?-client
-classic?WARN
-native?ERROR
-green?ERROR
在我們的jdk目錄中jre\bin\server和jre\bin\client都有JVM.dll文件存在,而Java正是通過JVM.cfg配置文件來管理這些不同版本的JVM.dll的.通過文件我們可以定義目前jdk中支持那些JVM,前面部分(client)是JVM名稱疫诽,后面是參數(shù)舅世,KNOWN表示JVM存在,ALIASED_TO表示給別的JVM取一個別名奇徒,WARN表示不存在時找一個JVM替代雏亚,ERROR表示不存在拋出異常.在運行javaXXX是,Java.exe會通過CheckJVMType來檢查當前的JVM類型摩钙,Java可以通過兩種參數(shù)的方式來指定具體的JVM類型罢低,一種按照JVM.cfg文件中的JVM名稱指定,第二種方法是直接指定胖笛,它們執(zhí)行的方法分別是“Java -J”网持、“Java -XXaltJVM=”或“Java -J-XXaltJVM=”。如果是第一種參數(shù)傳遞方式长踊,CheckJVMType函數(shù)會取參數(shù)‘-J’后面的JVM名稱功舀,然后從已知的JVM配置參數(shù)中查找如果找到同名的則去掉該JVM名稱前的‘-’直接返回該值;而第二種方法身弊,會直接返回“-XXaltJVM=”或“-J-XXaltJVM=”后面的JVM類型名稱辟汰;如果在運行Java時未指定上面兩種方法中的任一一種參數(shù),CheckJVMType會取配置文件中第一個配置中的JVM名稱阱佛,去掉名稱前面的‘-’返回該值帖汞。CheckJVMType函數(shù)的這個返回值會在下面的函數(shù)中匯同jre路徑組合成JVM.dll的絕對路徑。如果沒有指定這會使用JVM.cfg中第一個定義的JVM.可以通過set _Java_LAUNCHER_DEBUG=1在控制臺上測試.
最后獲得JVM.dll的路徑凑术,JRE路徑+\bin+\JVM類型字符串+\JVM.dll就是JVM的文件路徑了翩蘸,但是如果在調(diào)用Java程序時用-XXaltJVM=參數(shù)指定的路徑path,就直接用path+\JVM.dll文件做為JVM.dll的文件路徑.
二:裝載JVM.dll
通過第一步已經(jīng)找到了JVM的路徑,Java通過LoadJavaVM來裝入JVM.dll文件.裝入工作很簡單就是調(diào)用Windows API函數(shù):
LoadLibrary裝載JVM.dll動態(tài)連接庫.然后把JVM.dll中的導(dǎo)出函數(shù)JNI_CreateJavaVM和JNI_GetDefaultJavaVMInitArgs掛接到InvocationFunctions變量的CreateJavaVM和GetDefaultJavaVMInitArgs函數(shù)指針變量上淮逊。JVM.dll的裝載工作宣告完成催首。
三:初始化JVM扶踊,獲得本地調(diào)用接口,這樣就可以在Java中調(diào)用JVM的函數(shù)了.調(diào)用InvocationFunctions->CreateJavaVM也就是JVM中JNI_CreateJavaVM方法獲得JNIEnv結(jié)構(gòu)的實例.
四:運行Java程序.
Java程序有兩種方式一種是jar包翅帜,一種是class. 運行jar,Java -jar XXX.jar運行的時候姻檀,Java.exe調(diào)用GetMainClassName函數(shù)命满,該函數(shù)先獲得JNIEnv實例然后調(diào)用Java類Java.util.jar.JarFileJNIEnv中方法getManifest()并從返回的Manifest對象中取getAttributes("Main-Class")的值即jar包中文件:META-INF/MANIFEST.MF指定的Main-Class的主類名作為運行的主類涝滴。之后main函數(shù)會調(diào)用Java.c中LoadClass方法裝載該主類(使用JNIEnv實例的FindClass)。main函數(shù)直接調(diào)用Java.c中LoadClass方法裝載該類胶台。如果是執(zhí)行class方法歼疮。main函數(shù)直接調(diào)用Java.c中LoadClass方法裝載該類。
然后main函數(shù)調(diào)用JNIEnv實例的GetStaticMethodID方法查找裝載的class主類中
“public static void main(String[] args)”方法诈唬,并判斷該方法是否為public方法韩脏,然后調(diào)用JNIEnv實例的
CallStaticVoidMethod方法調(diào)用該Java類的main方法。
以下轉(zhuǎn)自:http://blog.csdn.net/cnhzgb/article/details/7179419
= GC 基礎(chǔ) =====================
JAVA堆的描述如下:
內(nèi)存由 Perm 和 Heap 組成. 其中
Heap = {Old + NEW = { Eden , from, to } }
JVM內(nèi)存模型中分兩大塊铸磅,一塊是 NEW Generation, 另一塊是Old Generation. 在New Generation中赡矢,有一個叫Eden的空間,主要是用來存放新生的對象阅仔,還有兩個Survivor Spaces(from,to), 它們用來存放每次垃圾回收后存活下來的對象吹散。在Old Generation中,主要存放應(yīng)用程序中生命周期長的內(nèi)存對象八酒,還有個Permanent Generation空民,主要用來放JVM自己的反射對象,比如類對象和方法對象等羞迷。
垃圾回收描述:
在New Generation塊中界轩,垃圾回收一般用Copying的算法,速度快衔瓮。每次GC的時候浊猾,存活下來的對象首先由Eden拷貝到某個Survivor Space, 當Survivor Space空間滿了后, 剩下的live對象就被直接拷貝到Old Generation中去。因此热鞍,每次GC后葫慎,Eden內(nèi)存塊會被清空。在Old Generation塊中碍现,垃圾回收一般用mark-compact的算法幅疼,速度慢些,但減少內(nèi)存要求.
垃圾回收分多級昼接,0級為全部(Full)的垃圾回收爽篷,會回收OLD段中的垃圾;1級或以上為部分垃圾回收慢睡,只會回收NEW中的垃圾逐工,內(nèi)存溢出通常發(fā)生于OLD段或Perm段垃圾回收后铡溪,仍然無內(nèi)存空間容納新的Java對象的情況。
當一個URL被訪問時泪喊,內(nèi)存申請過程如下:
A. JVM會試圖為相關(guān)Java對象在Eden中初始化一塊內(nèi)存區(qū)域
B. 當Eden空間足夠時棕硫,內(nèi)存申請結(jié)束。否則到下一步
C. JVM試圖釋放在Eden中所有不活躍的對象(這屬于1或更高級的垃圾回收), 釋放后若Eden空間仍然不足以放入新對象袒啼,則試圖將部分Eden中活躍對象放入Survivor區(qū)
D. Survivor區(qū)被用來作為Eden及OLD的中間交換區(qū)域哈扮,當OLD區(qū)空間足夠時,Survivor區(qū)的對象會被移到Old區(qū)蚓再,否則會被保留在Survivor區(qū)
E. 當OLD區(qū)空間不夠時滑肉,JVM會在OLD區(qū)進行完全的垃圾收集(0級)
F. 完全垃圾收集后,若Survivor及OLD區(qū)仍然無法存放從Eden復(fù)制過來的部分對象摘仅,導(dǎo)致JVM無法在Eden區(qū)為新對象創(chuàng)建內(nèi)存區(qū)域靶庙,則出現(xiàn)”out of memory錯誤”
JVM調(diào)優(yōu)建議:
ms/mx:定義YOUNG+OLD段的總尺寸,ms為JVM啟動時YOUNG+OLD的內(nèi)存大型奘簟六荒;mx為最大可占用的YOUNG+OLD內(nèi)存大小。在用戶生產(chǎn)環(huán)境上一般將這兩個值設(shè)為相同矾端,以減少運行期間系統(tǒng)在內(nèi)存申請上所花的開銷掏击。
NewSize/MaxNewSize:定義YOUNG段的尺寸,NewSize為JVM啟動時YOUNG的內(nèi)存大行氪病铐料;MaxNewSize為最大可占用的YOUNG內(nèi)存大小。在用戶生產(chǎn)環(huán)境上一般將這兩個值設(shè)為相同豺旬,以減少運行期間系統(tǒng)在內(nèi)存申請上所花的開銷钠惩。
PermSize/MaxPermSize:定義Perm段的尺寸,PermSize為JVM啟動時Perm的內(nèi)存大凶逶摹篓跛;MaxPermSize為最大可占用的Perm內(nèi)存大小。在用戶生產(chǎn)環(huán)境上一般將這兩個值設(shè)為相同坦刀,以減少運行期間系統(tǒng)在內(nèi)存申請上所花的開銷愧沟。
SurvivorRatio:設(shè)置Survivor空間和Eden空間的比例
內(nèi)存溢出的可能性
1. OLD段溢出
這種內(nèi)存溢出是最常見的情況之一,產(chǎn)生的原因可能是:
1) 設(shè)置的內(nèi)存參數(shù)過小(ms/mx, NewSize/MaxNewSize)
2) 程序問題
單個程序持續(xù)進行消耗內(nèi)存的處理鲤遥,如循環(huán)幾千次的字符串處理沐寺,對字符串處理應(yīng)建議使用StringBuffer。此時不會報內(nèi)存溢出錯盖奈,卻會使系統(tǒng)持續(xù)垃圾收集混坞,無法處理其它請求,相關(guān)問題程序可通過Thread Dump獲取(見系統(tǒng)問題診斷一章)單個程序所申請內(nèi)存過大究孕,有的程序會申請幾十乃至幾百兆內(nèi)存啥酱,此時JVM也會因無法申請到資源而出現(xiàn)內(nèi)存溢出,對此首先要找到相關(guān)功能厨诸,然后交予程序員修改镶殷,要找到相關(guān)程序,必須在Apache日志中尋找微酬。
當Java對象使用完畢后绘趋,其所引用的對象卻沒有銷毀,使得JVM認為他還是活躍的對象而不進行回收得封,這樣累計占用了大量內(nèi)存而無法釋放埋心。由于目前市面上還沒有對系統(tǒng)影響小的內(nèi)存分析工具,故此時只能和程序員一起定位忙上。
2. Perm段溢出
通常由于Perm段裝載了大量的Servlet類而導(dǎo)致溢出,目前的解決辦法:
1) 將PermSize擴大闲坎,一般256M能夠滿足要求
2) 若別無選擇疫粥,則只能將servlet的路徑加到CLASSPATH中枕扫,但一般不建議這么處理
3. C Heap溢出
系統(tǒng)對C Heap沒有限制瑟啃,故C Heap發(fā)生問題時,Java進程所占內(nèi)存會持續(xù)增長诬垂,直到占用所有可用系統(tǒng)內(nèi)存
其他:
JVM有2個GC線程绣溜。第一個線程負責回收Heap的Young區(qū)慷彤。第二個線程在Heap不足時,遍歷Heap怖喻,將Young 區(qū)升級為Older區(qū)底哗。Older區(qū)的大小等于-Xmx減去-Xmn,不能將-Xms的值設(shè)的過大锚沸,因為第二個線程被迫運行會降低JVM的性能跋选。
為什么一些程序頻繁發(fā)生GC?有如下原因:
l ? ? ? ? 程序內(nèi)調(diào)用了System.gc()或Runtime.gc()哗蜈。
l ? ? ? ? 一些中間件軟件調(diào)用自己的GC方法前标,此時需要設(shè)置參數(shù)禁止這些GC。
l ? ? ? ? Java的Heap太小距潘,一般默認的Heap值都很小炼列。
l ? ? ? ? 頻繁實例化對象,Release對象音比。此時盡量保存并重用對象俭尖,例如使用StringBuffer()和String()。
如果你發(fā)現(xiàn)每次GC后硅确,Heap的剩余空間會是總空間的50%目溉,這表示你的Heap處于健康狀態(tài)明肮。許多Server端的Java程序每次GC后最好能有65%的剩余空間。
經(jīng)驗之談:
1.Server端JVM最好將-Xms和-Xmx設(shè)為相同值缭付。為了優(yōu)化GC柿估,最好讓-Xmn值約等于-Xmx的1/3[2]。
2.一個GUI程序最好是每10到20秒間運行一次GC陷猫,每次在半秒之內(nèi)完成[2]秫舌。
注意:
1.增加Heap的大小雖然會降低GC的頻率,但也增加了每次GC的時間绣檬。并且GC運行時足陨,所有的用戶線程將暫停,也就是GC期間娇未,Java應(yīng)用程序不做任何工作墨缘。
2.Heap大小并不決定進程的內(nèi)存使用量。進程的內(nèi)存使用量要大于-Xmx定義的值零抬,因為Java為其他任務(wù)分配內(nèi)存镊讼,例如每個線程的Stack等。
2.Stack的設(shè)定
每個線程都有他自己的Stack平夜。
-Xss
每個線程的Stack大小
Stack的大小限制著線程的數(shù)量蝶棋。如果Stack過大就好導(dǎo)致內(nèi)存溢漏。-Xss參數(shù)決定Stack大小忽妒,例如-Xss1024K玩裙。如果Stack太小,也會導(dǎo)致Stack溢漏段直。
3.硬件環(huán)境
硬件環(huán)境也影響GC的效率吃溅,例如機器的種類,內(nèi)存坷牛,swap空間罕偎,和CPU的數(shù)量。
如果你的程序需要頻繁創(chuàng)建很多transient對象京闰,會導(dǎo)致JVM頻繁GC颜及。這種情況你可以增加機器的內(nèi)存,來減少Swap空間的使用[2]蹂楣。
4.4種GC
第一種為單線程GC俏站,也是默認的GC。痊土,該GC適用于單CPU機器肄扎。
第二種為Throughput GC,是多線程的GC,適用于多CPU犯祠,使用大量線程的程序旭等。第二種GC與第一種GC相似,不同在于GC在收集Young區(qū)是多線程的衡载,但在Old區(qū)和第一種一樣搔耕,仍然采用單線程。-XX:+UseParallelGC參數(shù)啟動該GC痰娱。
第三種為Concurrent Low Pause GC弃榨,類似于第一種,適用于多CPU梨睁,并要求縮短因GC造成程序停滯的時間鲸睛。這種GC可以在Old區(qū)的回收同時,運行應(yīng)用程序坡贺。-XX:+UseConcMarkSweepGC參數(shù)啟動該GC官辈。
第四種為Incremental Low Pause GC,適用于要求縮短因GC造成程序停滯的時間拴念。這種GC可以在Young區(qū)回收的同時钧萍,回收一部分Old區(qū)對象。-Xincgc參數(shù)啟動該GC政鼠。
按照基本回收策略分
引用計數(shù)(Reference Counting):
比較古老的回收算法。原理是此對象有一個引用队魏,即增加一個計數(shù)公般,刪除一個引用則減少一個計數(shù)。垃圾回收時胡桨,只用收集計數(shù)為0的對象官帘。此算法最致命的是無法處理循環(huán)引用的問題。
標記-清除(Mark-Sweep):
此算法執(zhí)行分兩階段昧谊。第一階段從引用根節(jié)點開始標記所有被引用的對象刽虹,第二階段遍歷整個堆,把未標記的對象清除呢诬。此算法需要暫停整個應(yīng)用涌哲,同時,會產(chǎn)生內(nèi)存碎片尚镰。
復(fù)制(Copying):
此算法把內(nèi)存空間劃為兩個相等的區(qū)域阀圾,每次只使用其中一個區(qū)域。垃圾回收時狗唉,遍歷當前使用區(qū)域初烘,把正在使用中的對象復(fù)制到另外一個區(qū)域中。算法每次只處理正在使用中的對象,因此復(fù)制成本比較小肾筐,同時復(fù)制過去以后還能進行相應(yīng)的內(nèi)存整理哆料,不會出現(xiàn)“碎片”問題。當然吗铐,此算法的缺點也是很明顯的东亦,就是需要兩倍內(nèi)存空間。
標記-整理(Mark-Compact):
此算法結(jié)合了“標記-清除”和“復(fù)制”兩個算法的優(yōu)點抓歼。也是分兩階段讥此,第一階段從根節(jié)點開始標記所有被引用對象,第二階段遍歷整個堆谣妻,把清除未標記對象并且把存活對象“壓縮”到堆的其中一塊萄喳,按順序排放。此算法避免了“標記-清除”的碎片問題蹋半,同時也避免了“復(fù)制”算法的空間問題他巨。
按分區(qū)對待的方式分
增量收集(Incremental Collecting):實時垃圾回收算法,即:在應(yīng)用進行的同時進行垃圾回收减江。不知道什么原因JDK5.0中的收集器沒有使用這種算法的染突。
分代收集(Generational Collecting):基于對對象生命周期分析后得出的垃圾回收算法。把對象分為年青代辈灼、年老代份企、持久代,對不同生命周期的對象使用不同的算法(上述方式中的一個)進行回收⊙灿ǎ現(xiàn)在的垃圾回收器(從J2SE1.2開始)都是使用此算法的司志。
按系統(tǒng)線程分
串行收集:串行收集使用單線程處理所有垃圾回收工作,因為無需多線程交互降宅,實現(xiàn)容易骂远,而且效率比較高。但是腰根,其局限性也比較明顯激才,即無法使用多處理器的優(yōu)勢,所以此收集適合單處理器機器额嘿。當然瘸恼,此收集器也可以用在小數(shù)據(jù)量(100M左右)情況下的多處理器機器上。
并行收集:并行收集使用多線程處理垃圾回收工作岩睁,因而速度快钞脂,效率高。而且理論上CPU數(shù)目越多捕儒,越能體現(xiàn)出并行收集器的優(yōu)勢冰啃。(串型收集的并發(fā)版本邓夕,需要暫停jvm) 并行paralise指的是多個任務(wù)在多個cpu中一起并行執(zhí)行,最后將結(jié)果合并阎毅。效率是N倍焚刚。
并發(fā)收集:相對于串行收集和并行收集而言,前面兩個在進行垃圾回收工作時扇调,需要暫停整個運行環(huán)境矿咕,而只有垃圾回收程序在運行,因此狼钮,系統(tǒng)在垃圾回收時會有明顯的暫停碳柱,而且暫停時間會因為堆越大而越長。(和并行收集不同熬芜,并發(fā)只有在開頭和結(jié)尾會暫停jvm)并發(fā)concurrent指的是多個任務(wù)在一個cpu偽同步執(zhí)行莲镣,但其實是串行調(diào)度的,效率并非直接是N倍涎拉。
分代垃圾回收
分代的垃圾回收策略瑞侮,是基于這樣一個事實:不同的對象的生命周期是不一樣的。因此鼓拧,不同生命周期的對象可以采取不同的收集方式半火,以便提高回收效率。
在Java程序運行的過程中季俩,會產(chǎn)生大量的對象钮糖,其中有些對象是與業(yè)務(wù)信息相關(guān),比如Http請求中的Session對象酌住、線程藐鹤、Socket連接,這類對象跟業(yè)務(wù)直接掛鉤赂韵,因此生命周期比較長。但是還有一些對象挠蛉,主要是程序運行過程中生成的臨時變量祭示,這些對象生命周期會比較短,比如:String對象谴古,由于其不變類的特性质涛,系統(tǒng)會產(chǎn)生大量的這些對象,有些對象甚至只用一次即可回收掰担。
試想汇陆,在不進行對象存活時間區(qū)分的情況下,每次垃圾回收都是對整個堆空間進行回收带饱,花費時間相對會長毡代,同時阅羹,因為每次回收都需要遍歷所有存活對象,但實際上教寂,對于生命周期長的對象而言捏鱼,這種遍歷是沒有效果的,因為可能進行了很多次遍歷酪耕,但是他們依舊存在导梆。因此,分代垃圾回收采用分治的思想迂烁,進行代的劃分看尼,把不同生命周期的對象放在不同代上,不同代上采用最適合它的垃圾回收方式進行回收盟步。
如圖所示:
虛擬機中的共劃分為三個代:年輕代(Young Generation)藏斩、年老點(Old Generation)和持久代(Permanent Generation)。其中持久代主要存放的是Java類的類信息址芯,與垃圾收集要收集的Java對象關(guān)系不大灾茁。年輕代和年老代的劃分是對垃圾收集影響比較大的。
年輕代:
所有新生成的對象首先都是放在年輕代的谷炸。年輕代的目標就是盡可能快速的收集掉那些生命周期短的對象北专。年輕代分三個區(qū)。一個Eden區(qū)旬陡,兩個Survivor區(qū)(一般而言)拓颓。大部分對象在Eden區(qū)中生成。當Eden區(qū)滿時描孟,還存活的對象將被復(fù)制到Survivor區(qū)(兩個中的一個)驶睦,當這個Survivor區(qū)滿時,此區(qū)的存活對象將被復(fù)制到另外一個Survivor區(qū)匿醒,當這個Survivor區(qū)也滿了的時候场航,從第一個Survivor區(qū)復(fù)制過來的并且此時還存活的對象,將被復(fù)制“年老區(qū)(Tenured)”廉羔。需要注意溉痢,Survivor的兩個區(qū)是對稱的,沒先后關(guān)系憋他,所以同一個區(qū)中可能同時存在從Eden復(fù)制過來 對象孩饼,和從前一個Survivor復(fù)制過來的對象,而復(fù)制到年老區(qū)的只有從第一個Survivor去過來的對象竹挡。而且镀娶,Survivor區(qū)總有一個是空的。同時揪罕,根據(jù)程序需要梯码,Survivor區(qū)是可以配置為多個的(多于兩個)宝泵,這樣可以增加對象在年輕代中的存在時間,減少被放到年老代的可能忍些。
年老代:
在年輕代中經(jīng)歷了N次垃圾回收后仍然存活的對象鲁猩,就會被放到年老代中。因此罢坝,可以認為年老代中存放的都是一些生命周期較長的對象廓握。
持久代:
用于存放靜態(tài)文件,如今Java類嘁酿、方法等隙券。持久代對垃圾回收沒有顯著影響,但是有些應(yīng)用可能動態(tài)生成或者調(diào)用一些class闹司,例如hibernate等娱仔,在這種時候需要設(shè)置一個比較大的持久代空間來存放這些運行過程中新增的類。持久代大小通過-XX:MaxPermSize=進行設(shè)置游桩。
什么情況下觸發(fā)垃圾回收
由于對象進行了分代處理牲迫,因此垃圾回收區(qū)域、時間也不一樣借卧。GC有兩種類型:Scavenge GC和Full GC盹憎。
Scavenge GC
一般情況下,當新對象生成铐刘,并且在Eden申請空間失敗時陪每,就會觸發(fā)Scavenge GC,對Eden區(qū)域進行GC镰吵,清除非存活對象檩禾,并且把尚且存活的對象移動到Survivor區(qū)。然后整理Survivor的兩個區(qū)疤祭。這種方式的GC是對年輕代的Eden區(qū)進行盼产,不會影響到年老代。因為大部分對象都是從Eden區(qū)開始的勺馆,同時Eden區(qū)不會分配的很大辆飘,所以Eden區(qū)的GC會頻繁進行。因而谓传,一般在這里需要使用速度快、效率高的算法芹关,使Eden去能盡快空閑出來续挟。
Full GC
對整個堆進行整理,包括Young侥衬、Tenured和Perm诗祸。Full GC因為需要對整個對進行回收跑芳,所以比Scavenge GC要慢,因此應(yīng)該盡可能減少Full GC的次數(shù)直颅。在對JVM調(diào)優(yōu)的過程中博个,很大一部分工作就是對于FullGC的調(diào)節(jié)。有如下原因可能導(dǎo)致Full GC:
· 年老代(Tenured)被寫滿
· 持久代(Perm)被寫滿
· System.gc()被顯示調(diào)用
·上一次GC之后Heap的各域分配策略動態(tài)變化
= G1 ===================================
傳說中的G1功偿,傳說中的low-pause垃圾收集盆佣。Java SE6的update14版本中已經(jīng)包含測試版,可以在啟動時加JVM參數(shù)來啟用
-XX:+UnlockExperimentalVMOptions -XX:+UseG1GC
http://www.blogjava.net/BlueDavy/archive/2009/03/11/259230.html
本文摘自《構(gòu)建高性能的大型分布式Java應(yīng)用》一書械荷,Garbage First簡稱G1共耍,它的目標是要做到盡量減少GC所導(dǎo)致的應(yīng)用暫停的時間,讓應(yīng)用達到準實時的效果吨瞎,同時保持JVM堆空間的利用率痹兜,將作為CMS的替代者在JDK 7中閃亮登場,其最大的特色在于允許指定在某個時間段內(nèi)GC所導(dǎo)致的應(yīng)用暫停的時間最大為多少颤诀,例如在100秒內(nèi)最多允許GC導(dǎo)致的應(yīng)用暫停時間為1秒字旭,這個特性對于準實時響應(yīng)的系統(tǒng)而言非常的吸引人,這樣就再也不用擔心系統(tǒng)突然會暫停個兩三秒了崖叫。
G1要做到這樣的效果遗淳,也是有前提的,一方面是硬件環(huán)境的要求归露,必須是多核的CPU以及較大的內(nèi)存(從規(guī)范來看洲脂,512M以上就滿足條件了),另外一方面是需要接受吞吐量的稍微降低剧包,對于實時性要求高的系統(tǒng)而言恐锦,這點應(yīng)該是可以接受的。
為了能夠達到這樣的效果疆液,G1在原有的各種GC策略上進行了吸收和改進一铅,在G1中可以看到增量收集器和CMS的影子,但它不僅僅是吸收原有GC策略的優(yōu)點堕油,并在此基礎(chǔ)上做出了很多的改進潘飘,簡單來說,G1吸收了增量GC以及CMS的精髓掉缺,將整個jvm Heap劃分為多個固定大小的region卜录,掃描時采用Snapshot-at-the-beginning的并發(fā)marking算法(具體在后面內(nèi)容詳細解釋)對整個heap中的region進行mark,回收時根據(jù)region中活躍對象的bytes進行排序眶明,首先回收活躍對象bytes小以及回收耗時短(預(yù)估出來的時間)的region艰毒,回收的方法為將此region中的活躍對象復(fù)制到另外的region中,根據(jù)指定的GC所能占用的時間來估算能回收多少region搜囱,這點和以前版本的Full GC時得處理整個heap非常不同丑瞧,這樣就做到了能夠盡量短時間的暫停應(yīng)用柑土,又能回收內(nèi)存,由于這種策略在回收時首先回收的是垃圾對象所占空間最多的region绊汹,因此稱為Garbage First鞭莽。
看完上面對于G1策略的簡短描述溅漾,并不能清楚的掌握G1,在繼續(xù)詳細看G1的步驟之前,必須先明白G1對于JVM Heap的改造琳状,這些對于習(xí)慣了劃分為new generation侮措、old generation的大家來說都有不少的新意肚吏。
G1將Heap劃分為多個固定大小的region涎劈,這也是G1能夠?qū)崿F(xiàn)控制GC導(dǎo)致的應(yīng)用暫停時間的前提,region之間的對象引用通過remembered set來維護典鸡,每個region都有一個remembered set被廓,remembered set中包含了引用當前region中對象的region的對象的pointer,由于同時應(yīng)用也會造成這些region中對象的引用關(guān)系不斷的發(fā)生改變萝玷,G1采用了Card Table來用于應(yīng)用通知region修改remembered sets嫁乘,Card Table由多個512字節(jié)的Card構(gòu)成,這些Card在Card Table中以1個字節(jié)來標識球碉,每個應(yīng)用的線程都有一個關(guān)聯(lián)的remembered set log蜓斧,用于緩存和順序化線程運行時造成的對于card的修改,另外睁冬,還有一個全局的filled RS buffers挎春,當應(yīng)用線程執(zhí)行時修改了card后,如果造成的改變僅為同一region中的對象之間的關(guān)聯(lián)豆拨,則不記錄remembered set log直奋,如造成的改變?yōu)榭鐁egion中的對象的關(guān)聯(lián),則記錄到線程的remembered set log施禾,如線程的remembered set log滿了脚线,則放入全局的filled RS buffers中,線程自身則重新創(chuàng)建一個新的remembered set log弥搞,remembered set本身也是一個由一堆cards構(gòu)成的哈希表邮绿。
盡管G1將Heap劃分為了多個region,但其默認采用的仍然是分代的方式攀例,只是僅簡單的劃分為了年輕代(young)和非年輕代船逮,這也是由于G1仍然堅信大多數(shù)新創(chuàng)建的對象都是不需要長的生命周期的,對于應(yīng)用新創(chuàng)建的對象粤铭,G1將其放入標識為young的region中傻唾,對于這些region,并不記錄remembered set logs,掃描時只需掃描活躍的對象冠骄,G1在分代的方式上還可更細的劃分為:fully young或partially young,fully young方式暫停的時候僅處理young regions加袋,partially同樣處理所有的young regions凛辣,但它還會根據(jù)允許的GC的暫停時間來決定是否要加入其他的非young regions,G1是運行到fully-young方式還是partially young方式职烧,外部是不能決定的扁誓,在啟動時,G1采用的為fully-young方式蚀之,當G1完成一次Concurrent Marking后蝗敢,則切換為partially young方式,隨后G1跟蹤每次回收的效率足删,如果回收fully-young中的regions已經(jīng)可以滿足內(nèi)存需要的話寿谴,那么就切換回fully young方式,但當heap size的大小接近滿的情況下失受,G1會切換到partially young方式讶泰,以保證能提供足夠的內(nèi)存空間給應(yīng)用使用。
除了分代方式的劃分外拂到,G1還支持另外一種pure G1的方式痪署,也就是不進行代的劃分,pure方式和分代方式的具體不同在下面的具體執(zhí)行步驟中進行描述兄旬。
掌握了這些概念后狼犯,繼續(xù)來看G1的具體執(zhí)行步驟:
1.?????????Initial Marking
G1對于每個region都保存了兩個標識用的bitmap,一個為previous marking bitmap领铐,一個為next marking bitmap悯森,bitmap中包含了一個bit的地址信息來指向?qū)ο蟮钠鹗键c。
開始Initial Marking之前罐孝,首先并發(fā)的清空next marking bitmap呐馆,然后停止所有應(yīng)用線程,并掃描標識出每個region中root可直接訪問到的對象莲兢,將region中top的值放入next top at mark start(TAMS)中汹来,之后恢復(fù)所有應(yīng)用線程。
觸發(fā)這個步驟執(zhí)行的條件為:
l??G1定義了一個JVM Heap大小的百分比的閥值改艇,稱為h收班,另外還有一個H,H的值為(1-h)*Heap Size谒兄,目前這個h的值是固定的摔桦,后續(xù)G1也許會將其改為動態(tài)的,根據(jù)jvm的運行情況來動態(tài)的調(diào)整,在分代方式下邻耕,G1還定義了一個u以及soft limit鸥咖,soft limit的值為H-u*Heap Size,當Heap中使用的內(nèi)存超過了soft limit值時兄世,就會在一次clean up執(zhí)行完畢后在應(yīng)用允許的GC暫停時間范圍內(nèi)盡快的執(zhí)行此步驟啼辣;
l??在pure方式下,G1將marking與clean up組成一個環(huán)御滩,以便clean up能充分的使用marking的信息鸥拧,當clean up開始回收時,首先回收能夠帶來最多內(nèi)存空間的regions削解,當經(jīng)過多次的clean up富弦,回收到?jīng)]多少空間的regions時,G1重新初始化一個新的marking與clean up構(gòu)成的環(huán)氛驮。
2.?????????Concurrent Marking
按照之前Initial Marking掃描到的對象進行遍歷腕柜,以識別這些對象的下層對象的活躍狀態(tài),對于在此期間應(yīng)用線程并發(fā)修改的對象的以來關(guān)系則記錄到remembered set logs中柳爽,新創(chuàng)建的對象則放入比top值更高的地址區(qū)間中媳握,這些新創(chuàng)建的對象默認狀態(tài)即為活躍的,同時修改top值磷脯。
3.?????????Final Marking Pause
當應(yīng)用線程的remembered set logs未滿時蛾找,是不會放入filled RS buffers中的,在這樣的情況下赵誓,這些remebered set logs中記錄的card的修改就會被更新了打毛,因此需要這一步,這一步要做的就是把應(yīng)用線程中存在的remembered set logs的內(nèi)容進行處理俩功,并相應(yīng)的修改remembered sets幻枉,這一步需要暫停應(yīng)用,并行的運行诡蜓。
4.?????????Live Data Counting and Cleanup
值得注意的是熬甫,在G1中,并不是說Final Marking Pause執(zhí)行完了蔓罚,就肯定執(zhí)行Cleanup這步的椿肩,由于這步需要暫停應(yīng)用,G1為了能夠達到準實時的要求豺谈,需要根據(jù)用戶指定的最大的GC造成的暫停時間來合理的規(guī)劃什么時候執(zhí)行Cleanup郑象,另外還有幾種情況也是會觸發(fā)這個步驟的執(zhí)行的:
l??G1采用的是復(fù)制方法來進行收集,必須保證每次的”to space”的空間都是夠的茬末,因此G1采取的策略是當已經(jīng)使用的內(nèi)存空間達到了H時厂榛,就執(zhí)行Cleanup這個步驟;
l??對于full-young和partially-young的分代模式的G1而言,則還有情況會觸發(fā)Cleanup的執(zhí)行击奶,full-young模式下辈双,G1根據(jù)應(yīng)用可接受的暫停時間、回收young regions需要消耗的時間來估算出一個yound regions的數(shù)量值柜砾,當JVM中分配對象的young regions的數(shù)量達到此值時辐马,Cleanup就會執(zhí)行;partially-young模式下局义,則會盡量頻繁的在應(yīng)用可接受的暫停時間范圍內(nèi)執(zhí)行Cleanup,并最大限度的去執(zhí)行non-young regions的Cleanup冗疮。
這一步中GC線程并行的掃描所有region萄唇,計算每個region中低于next TAMS值中marked data的大小,然后根據(jù)應(yīng)用所期望的GC的短延時以及G1對于region回收所需的耗時的預(yù)估术幔,排序region另萤,將其中活躍的對象復(fù)制到其他region中。
G1為了能夠盡量的做到準實時的響應(yīng)诅挑,例如估算暫停時間的算法四敞、對于經(jīng)常被引用的對象的特殊處理等,G1為了能夠讓GC既能夠充分的回收內(nèi)存拔妥,又能夠盡量少的導(dǎo)致應(yīng)用的暫停忿危,可謂費盡心思,從G1的論文中的性能評測來看效果也是不錯的没龙,不過如果G1能允許開發(fā)人員在編寫代碼時指定哪些對象是不用mark的就更完美了铺厨,這對于有巨大緩存的應(yīng)用而言,會有很大的幫助硬纤,G1將隨JDK 6 Update 14?beta發(fā)布解滓。
= CMS ==================================
http://www.iteye.com/topic/1119491
1.總體介紹:
CMS(Concurrent Mark-Sweep)是以犧牲吞吐量為代價來獲得最短回收停頓時間的垃圾回收器。并發(fā)意味著除了開頭和結(jié)束階段筝家,需要暫停JVM洼裤,其它時間gc和應(yīng)用一起執(zhí)行。對于要求服務(wù)器響應(yīng)速度的應(yīng)用上溪王,這種垃圾回收器非常適合腮鞍。在啟動JVM參數(shù)加上-XX:+UseConcMarkSweepGC,這個參數(shù)表示對于老年代的回收采用CMS在扰。CMS采用的基礎(chǔ)算法是:標記—清除缕减。默認會開啟 -XX :+UseParNewGC,在年輕代使用并行復(fù)制收集芒珠。
2.CMS過程:
初始標記(STW initial mark)
并發(fā)標記(Concurrent marking)
并發(fā)預(yù)清理(Concurrent precleaning)
重新標記(STW remark)
并發(fā)清理(Concurrent sweeping)
并發(fā)重置(Concurrent reset)
初始標記:在這個階段桥狡,需要虛擬機停頓正在執(zhí)行的任務(wù),官方的叫法STW(Stop The Word)。這個過程從垃圾回收的"根對象"開始裹芝,只掃描到能夠和"根對象"直接關(guān)聯(lián)的對象部逮,并作標記。所以這個過程雖然暫停了整個JVM嫂易,但是很快就完成了兄朋。
并發(fā)標記:這個階段緊隨初始標記階段,在初始標記的基礎(chǔ)上繼續(xù)向下追溯標記怜械。并發(fā)標記階段颅和,應(yīng)用程序的線程和并發(fā)標記的線程并發(fā)執(zhí)行,所以用戶不會感受到停頓缕允。
并發(fā)預(yù)清理:并發(fā)預(yù)清理階段仍然是并發(fā)的峡扩。在這個階段,虛擬機查找在執(zhí)行并發(fā)標記階段新進入老年代的對象(可能會有一些對象從新生代晉升到老年代障本, 或者有一些對象被分配到老年代)教届。通過重新掃描,減少下一個階段"重新標記"的工作驾霜,因為下一個階段會Stop The World案训。
重新標記:這個階段會暫停虛擬機,收集器線程掃描在CMS堆中剩余的對象粪糙。掃描從"跟對象"開始向下追溯强霎,并處理對象關(guān)聯(lián)。
并發(fā)清理:清理垃圾對象猜旬,這個階段收集器線程和應(yīng)用程序線程并發(fā)執(zhí)行脆栋。
并發(fā)重置:這個階段,重置CMS收集器的數(shù)據(jù)結(jié)構(gòu)洒擦,等待下一次垃圾回收椿争。
CSM執(zhí)行過程:
3.CMS缺點
CMS回收器采用的基礎(chǔ)算法是Mark-Sweep。所有CMS不會整理熟嫩、壓縮堆空間秦踪。這樣就會有一個問題:經(jīng)過CMS收集的堆會產(chǎn)生空間碎片。 CMS不對堆空間整理壓縮節(jié)約了垃圾回收的停頓時間掸茅,但也帶來的堆空間的浪費椅邓。為了解決堆空間浪費問題,CMS回收器不再采用簡單的指針指向一塊可用堆空 間來為下次對象分配使用昧狮。而是把一些未分配的空間匯總成一個列表景馁,當JVM分配對象空間的時候,會搜索這個列表找到足夠大的空間來hold住這個對象逗鸣。
需要更多的CPU資源合住。從上面的圖可以看到绰精,為了讓應(yīng)用程序不停頓,CMS線程和應(yīng)用程序線程并發(fā)執(zhí)行透葛,這樣就需要有更多的CPU笨使,單純靠線程切 換是不靠譜的。并且僚害,重新標記階段硫椰,為空保證STW快速完成,也要用到更多的甚至所有的CPU資源萨蚕。當然靶草,多核多CPU也是未來的趨勢!
CMS的另一個缺點是它需要更大的堆空間岳遥。因為CMS標記階段應(yīng)用程序的線程還是在執(zhí)行的爱致,那么就會有堆空間繼續(xù)分配的情況,為了保證在CMS回 收完堆之前還有空間分配給正在運行的應(yīng)用程序寒随,必須預(yù)留一部分空間。也就是說帮坚,CMS不會在老年代滿的時候才開始收集妻往。相反,它會嘗試更早的開始收集试和,已 避免上面提到的情況:在回收完成之前讯泣,堆沒有足夠空間分配!默認當老年代使用68%的時候阅悍,CMS就開始行動了好渠。 – XX:CMSInitiatingOccupancyFraction =n 來設(shè)置這個閥值。
總得來說节视,CMS回收器減少了回收的停頓時間拳锚,但是降低了堆空間的利用率。
4.啥時候用CMS
如果你的應(yīng)用程序?qū)νnD比較敏感寻行,并且在應(yīng)用程序運行的時候可以提供更大的內(nèi)存和更多的CPU(也就是硬件牛逼)霍掺,那么使用CMS來收集會給你帶來好處。還有拌蜘,如果在JVM中杆烁,有相對較多存活時間較長的對象(老年代比較大)會更適合使用CMS。
= 調(diào)試工具 ==================================
jmap
jmap -heap pid ?(不能觀察G1模式)
using parallel threads in the new generation.
using thread-local object allocation.
Concurrent Mark-Sweep GC
Heap Configuration:
MinHeapFreeRatio = 40
MaxHeapFreeRatio = 70
MaxHeapSize ? ? ?= 2147483648 (2048.0MB)
NewSize ? ? ? ? ?= 268435456 (256.0MB)
MaxNewSize ? ? ? = 268435456 (256.0MB)
OldSize ? ? ? ? ?= 805306368 (768.0MB)
NewRatio ? ? ? ? = 7
SurvivorRatio ? ?= 8
PermSize ? ? ? ? = 134217728 (128.0MB)
MaxPermSize ? ? ?= 134217728 (128.0MB)
Heap Usage:
New Generation (Eden + 1 Survivor Space):
capacity = 241631232 (230.4375MB)
used ? ? = 145793088 (139.03912353515625MB)
free ? ? = 95838144 (91.39837646484375MB)
60.33702133340114% used
Eden Space:
capacity = 214827008 (204.875MB)
used ? ? = 132689456 (126.54252624511719MB)
free ? ? = 82137552 (78.33247375488281MB)
61.7657236095752% used
From Space:
capacity = 26804224 (25.5625MB)
used ? ? = 13103632 (12.496597290039062MB)
free ? ? = 13700592 (13.065902709960938MB)
48.886444166411984% used
To Space:
capacity = 26804224 (25.5625MB)
used ? ? = 0 (0.0MB)
free ? ? = 26804224 (25.5625MB)
0.0% used
concurrent mark-sweep generation: (old區(qū))
capacity = 1879048192 (1792.0MB)
used ? ? = 1360638440 (1297.6059341430664MB)
free ? ? = 518409752 (494.3940658569336MB)
72.41104543209076% used
Perm Generation:
capacity = 134217728 (128.0MB)
used ? ? = 65435064 (62.40373992919922MB)
free ? ? = 68782664 (65.59626007080078MB)
48.75292181968689% used
jmap -histo:live pid
num ? ? #instances ? ? ? ? #bytes ?class name
----------------------------------------------
1: ? ? ? 3148147 ? ? ?209172848 ?[B
2: ? ? ? 2584345 ? ? ?144723320 ?java.lang.ref.SoftReference
3: ? ? ? 2578827 ? ? ?123783696 ?sun.misc.CacheEntry
4: ? ? ? ?781560 ? ? ?112544640 ?com.sun.NET.ssl.internal.ssl.SSLSessionImpl
5: ? ? ? 1385200 ? ? ? 89970592 ?[C
6: ? ? ? ?783287 ? ? ? 87807200 ?[Ljava.util.Hashtable$Entry;
7: ? ? ? 1421399 ? ? ? 56855960 ?java.lang.String
8: ? ? ? ? ? ?12 ? ? ? 56828880 ?[Lsun.misc.CacheEntry;
9: ? ? ? 2343358 ? ? ? 56240592 ?com.sun.net.ssl.internal.ssl.SessionId
10: ? ? ? ?783185 ? ? ? 50123840 ?java.util.Hashtable
11: ? ? ? ?783094 ? ? ? 50118016 ?java.lang.ref.Finalizer
12: ? ? ? ?287243 ? ? ? 36086720 ?[Ljava.lang.Object;
13: ? ? ? ?263376 ? ? ? 33712128 ?org.apache.commons.pool.impl.GenericObjectPool
jstat
jstat -gccause 31169 60000 1000
(sweep 1,2) (Eden) (Old) (Perm) (Young GC, GCTime)(Full GC, GCTime)
S0 ? ? ? ? S1 ? ? E ? ? ? ? O ? ? ? ? P ? ? YGC ? ? YGCT ? ? FGC ? ?FGCT ? ? ?GCT ? ? ? ? ? ? LGCC ? ? ? ? ? ? ? ? GCC
48.80 ? 0.00 ?68.94 ?69.55 ?48.86 ?30202 ?725.319 51835 5083.298 5808.616 unknown GCCause ? ? ?No GC
47.98 ? 0.00 ?37.47 ?69.61 ?48.86 ?30206 ?725.385 51835 5083.298 5808.682 unknown GCCause ? ? ?No GC
50.73 ? 0.00 ?51.72 ?69.65 ?48.86 ?30210 ?725.459 51835 5083.298 5808.757 unknown GCCause ? ? ?No GC
0.00 ?50.02 ?82.67 ?69.60 ?48.84 ?30213 ?725.508 51836 5091.572 5817.081 unknown GCCause ? ? ?No GC
jstat -gcutil $pid
S0 ? ? ? ? S1 ? ? E ? ? ? ? O ? ? ? P ? ? ? YGC ? ? YGCT ? ?FGC ? ?FGCT ? ? GCT
74.79 ? 0.00 ?95.15 ? 0.86 ?37.35 ? ? ?2 ? ? ? ?0.112 ? ? 0 ? ? ? ?0.000 ? ? ?0.112
O = old occupied
YGC = young gc time ( new part )
YGCT = young gc total cost time
FGC = full gc time ( old part )
FGCT = full gc total cost time
GCT = all gc cost time
jvisualvm
window下啟動遠程監(jiān)控简卧,并在被監(jiān)控服務(wù)端兔魂,啟動jstatd服務(wù)。
創(chuàng)建安全策略文件举娩,并命名為jstatd.all.policy
grant codebase "file:${java.home}/../lib/tools.jar" {
permission java.security.AllPermission;
};
jstatd -J-Djava.security.policy=jstatd.all.policy -p 8080 &
======================== Tunning =================
典型配置:
-server -Xmx2g -Xms2g -Xmn512m -XX:PermSize=128m -Xss256k -XX:+DisableExplicitGC -XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:+CMSParallelRemarkEnabled -XX:+UseCMSCompactAtFullCollection -XX:LargePageSizeInBytes=128m -XX:+UseFastAccessorMethods -XX:+UseCMSInitiatingOccupancyOnly -XX:CMSInitiatingOccupancyFraction=70 -Djava.awt.headless=true -Djava.net.preferIPv4Stack=true
http://java.sun.com/performance/reference/whitepapers/tuning.html
http://www.oracle.com/technetwork/java/gc-tuning-5-138395.html
+AggressiveOpts 激進優(yōu)化析校,默認開啟构罗,使用java新特性優(yōu)化
1. 默認使用串行收集器, 單個cpu時適用
2. 吞吐收集器(throughput collector):命令行參數(shù):-XX:+UseParallelGC勺良。在新生代使用并行清除收集策略绰播,在舊生代和默認收集器相同。
適用:a尚困、擁有2個以上cpu蠢箩, b、臨時對象較多的程序
-XX:ParallelGCThreads 并行收集線程數(shù)量事甜,最好和cpu數(shù)量相當
3. 并發(fā)收集器(concurrent low pause collector):命令行參數(shù):-XX:+UseConcMarkSweepGC谬泌。在舊生代使用并發(fā)收集策略,大部分收集工作都是和應(yīng)用并發(fā)進行的逻谦,在進行收集的時候掌实,應(yīng)用的暫停時間很短。默認配套打開 -XX:+UseParNewGC邦马,會在新生代使用并行復(fù)制收集贱鼻。
適用:a、擁有多個cpu滋将, b邻悬、老對象較多的程序
如果使用了UseParNewGC,那么同時使用CMSParallelRemarkEnabled參數(shù)可以降低標識暫停
-XX:+UseCMSCompactAtFullCollection:打開對年老代的壓縮随闽「阜幔可能會影響性能,但是可以消除碎片
-XX:+UseFastAccessorMethods 原始類型的快速優(yōu)化
-XX:SurvivorRatio 新生區(qū)中掘宪,eden&survivor的比例蛾扇,設(shè)置為8
-XX:TargetSurvivorRatio 生存區(qū)需要做垃圾回收的比例值,默認為50%魏滚,設(shè)置高些可以更好的利用該區(qū)
各個垃圾收集器之間的區(qū)別:
http://www.javaperformancetuning.com/news/qotm026.shtml
新生代镀首,單獨區(qū)域單獨收集,不會影響老生代鼠次,因為區(qū)域小蘑斧,且允許漏收集,采用復(fù)制清除的方法须眷,更快竖瘾。
The(original) copying collector(Enabled by default). When this collector kicks in, all application threads are stopped, and the copying collection proceeds using one thread (which means only one CPU even if on a multi-CPU machine). This is known as a stop-the-world collection, because basically the JVM pauses everything else until the collection is completed.
Theparallel copying collector(Enabled using -XX:+UseParNewGC). Like the original copying collector, this is a stop-the-world collector. However this collector parallelizes the copying collection over multiple threads, which is more efficient than the original single-thread copying collector for multi-CPU machines (though not for single-CPU machines). This algorithm potentially speeds up young generation collection by a factor equal to the number of CPUs available, when compared to the original singly-threaded copying collector.
Theparallel scavenge collector(Enabled using -XX:UseParallelGC). This is like the previous parallel copying collector, but the algorithm is tuned for gigabyte heaps (over 10GB) on multi-CPU machines. This collection algorithm is designed to maximize throughput while minimizing pauses. It has an optional adaptive tuning policy which will automatically resize heap spaces. If you use this collector, you can only use the the original mark-sweep collector in the old generation (i.e. the newer old generation concurrent collector cannot work with this young generation collector).
UserParallelGC使用了更高效的算法,用于處理大規(guī)模內(nèi)存>10G場景花颗,提供了大吞吐量功能捕传。但是,同時在老生代扩劝,只能使用串行的標記清除方法庸论。
老生代职辅,必須做fullgc,必須從root開始全面標識收集聂示。
The(original) mark-sweep collector(Enabled by default). This uses a stop-the-world mark-and-sweep collection algorithm. The collector is single-threaded, the entire JVM is paused and the collector uses only one CPU until completed.
Theconcurrent collector(Enabled using -XX:+UseConcMarkSweepGC). This collector tries to allow application processing to continue as much as possible during the collection. Splitting the collection into six phases described shortly, four are concurrent while two are stop-the-world:
1. the initial-mark phase (stop-the-world, snapshot the old generation so that we can run most of the rest of the collection concurrent to the application threads);
2. the mark phase (concurrent, mark the live objects traversing the object graph from the roots);
3. the pre-cleaning phase (concurrent);
4. the re-mark phase (stop-the-world, another snapshot to capture any changes to live objects since the collection started);
5. the sweep phase (concurrent, recycles memory by clearing unreferenced objects);
6. the reset phase (concurrent).
If "the rate of creation" of objects is too high, and the concurrent collector is not able to keep up with the concurrent collection, it falls back to the traditional mark-sweep collector.
Theincremental collector(Enabled using -Xincgc). The incremental collector uses a "train" algorithm to collect small portions of the old generation at a time. This collector has higher overheads than the mark-sweep collector, but because small numbers of objects are collected each time, the (stop-the-world) garbage collection pause is minimized at the cost of total garbage collection taking longer. The "train" algorithm does not guarantee a maximum pause time, but pause times are typically less than ten milliseconds.