本文首先簡(jiǎn)單介紹了垃圾收集的常見(jiàn)方式勺阐,然后再分析了G1收集器的收集原理卷中,相比其他垃圾收集器的優(yōu)勢(shì),最后給出了一些調(diào)優(yōu)實(shí)踐渊抽。
一蟆豫,什么是垃圾回收
首先,在了解G1之前懒闷,我們需要清楚的知道十减,垃圾回收是什么?簡(jiǎn)單的說(shuō)垃圾回收就是回收內(nèi)存中不再使用的對(duì)象愤估。
垃圾回收的基本步驟
回收的步驟有2步:
- 查找內(nèi)存中不再使用的對(duì)象
- 釋放這些對(duì)象占用的內(nèi)存
1,查找內(nèi)存中不再使用的對(duì)象
那么問(wèn)題來(lái)了嫉称,如何判斷哪些對(duì)象不再被使用呢?我們也有2個(gè)方法:
- 引用計(jì)數(shù)法
引用計(jì)數(shù)法就是如果一個(gè)對(duì)象沒(méi)有被任何引用指向灵疮,則可視之為垃圾织阅。這種方法的缺點(diǎn)就是不能檢測(cè)到環(huán)的存在。
2.根搜索算法
根搜索算法的基本思路就是通過(guò)一系列名為”GC Roots”的對(duì)象作為起始點(diǎn)震捣,從這些節(jié)點(diǎn)開(kāi)始向下搜索荔棉,搜索所走過(guò)的路徑稱為引用鏈(Reference Chain),當(dāng)一個(gè)對(duì)象到GC Roots沒(méi)有任何引用鏈相連時(shí)蒿赢,則證明此對(duì)象是不可用的润樱。
現(xiàn)在我們已經(jīng)知道如何找出垃圾對(duì)象了,如何把這些對(duì)象清理掉呢羡棵?
2. 釋放這些對(duì)象占用的內(nèi)存
常見(jiàn)的方式有復(fù)制或者直接清理壹若,但是直接清理會(huì)存在內(nèi)存碎片,于是就會(huì)產(chǎn)生了清理再壓縮的方式。
總得來(lái)說(shuō)就產(chǎn)生了三種類型的回收算法店展。
1.標(biāo)記-復(fù)制
它將可用內(nèi)存容量劃分為大小相等的兩塊养篓,每次只使用其中的一塊。當(dāng)這一塊用完之后赂蕴,就將還存活的對(duì)象復(fù)制到另外一塊上面柳弄,然后在把已使用過(guò)的內(nèi)存空間一次理掉。它的優(yōu)點(diǎn)是實(shí)現(xiàn)簡(jiǎn)單概说,效率高碧注,不會(huì)存在內(nèi)存碎片。缺點(diǎn)就是需要2倍的內(nèi)存來(lái)管理糖赔。
2.標(biāo)記-清理
標(biāo)記清除算法分為“標(biāo)記”和“清除”兩個(gè)階段:首先標(biāo)記出需要回收的對(duì)象萍丐,標(biāo)記完成之后統(tǒng)一清除對(duì)象。它的優(yōu)點(diǎn)是效率高放典,缺點(diǎn)是容易產(chǎn)生內(nèi)存碎片逝变。
3.標(biāo)記-整理
標(biāo)記操作和“標(biāo)記-清理”算法一致,后續(xù)操作不只是直接清理對(duì)象刻撒,而是在清理無(wú)用對(duì)象完成后讓所有 存活的對(duì)象都向一端移動(dòng)骨田,并更新引用其對(duì)象的指針耿导。因?yàn)橐苿?dòng)對(duì)象声怔,所以它的效率要比“標(biāo)記-清理”效率低,但是不會(huì)產(chǎn)生內(nèi)存碎片舱呻。
基于分代的假設(shè)
由于對(duì)象的存活時(shí)間有長(zhǎng)有短醋火,所以對(duì)于存活時(shí)間長(zhǎng)的對(duì)象,減少被gc的次數(shù)可以避免不必要的開(kāi)銷箱吕。這樣我們就把內(nèi)存分成新生代和老年代芥驳,新生代存放剛創(chuàng)建的和存活時(shí)間比較短的對(duì)象,老年代存放存活時(shí)間比較長(zhǎng)的對(duì)象茬高。這樣每次僅僅清理年輕代兆旬,老年代僅在必要時(shí)時(shí)再做清理可以極大的提高GC效率,節(jié)省GC時(shí)間怎栽。
java垃圾收集器的歷史
第一階段丽猬,Serial(串行)收集器
在jdk1.3.1之前,java虛擬機(jī)僅僅能使用Serial收集器熏瞄。 Serial收集器是一個(gè)單線程的收集器脚祟,但它的“單線程”的意義并不僅僅是說(shuō)明它只會(huì)使用一個(gè)CPU或一條收集線程去完成垃圾收集工作,更重要的是在它進(jìn)行垃圾收集時(shí)强饮,必須暫停其他所有的工作線程由桌,直到它收集結(jié)束。
PS:開(kāi)啟Serial收集器的方式
-XX:+UseSerialGC
第二階段,Parallel(并行)收集器
Parallel收集器也稱吞吐量收集器行您,相比Serial收集器铭乾,Parallel最主要的優(yōu)勢(shì)在于使用多線程去完成垃圾清理工作,這樣可以充分利用多核的特性邑雅,大幅降低gc時(shí)間片橡。
PS:開(kāi)啟Parallel收集器的方式
-XX:+UseParallelGC -XX:+UseParallelOldGC
第三階段,CMS(并發(fā))收集器
CMS收集器在Minor GC時(shí)會(huì)暫停所有的應(yīng)用線程淮野,并以多線程的方式進(jìn)行垃圾回收捧书。在Full GC時(shí)不再暫停應(yīng)用線程,而是使用若干個(gè)后臺(tái)線程定期的對(duì)老年代空間進(jìn)行掃描骤星,及時(shí)回收其中不再使用的對(duì)象经瓷。
PS:開(kāi)啟CMS收集器的方式
-XX:+UseParNewGC -XX:+UseConcMarkSweepGC
第四階段,G1(并發(fā))收集器
G1收集器(或者垃圾優(yōu)先收集器)的設(shè)計(jì)初衷是為了盡量縮短處理超大堆(大于4GB)時(shí)產(chǎn)生的停頓洞难。相對(duì)于CMS的優(yōu)勢(shì)而言是內(nèi)存碎片的產(chǎn)生率大大降低舆吮。
PS:開(kāi)啟G1收集器的方式
-XX:+UseG1GC
二,了解G1
G1的第一篇paper(附錄1)發(fā)表于2004年队贱,在2012年才在jdk1.7u4中可用色冀。oracle官方計(jì)劃在jdk9中將G1變成默認(rèn)的垃圾收集器,以替代CMS柱嫌。為何oracle要極力推薦G1呢锋恬,G1有哪些優(yōu)點(diǎn)?
首先编丘,G1的設(shè)計(jì)原則就是簡(jiǎn)單可行的性能調(diào)優(yōu)
開(kāi)發(fā)人員僅僅需要聲明以下參數(shù)即可:
-XX:+UseG1GC -Xmx32g -XX:MaxGCPauseMillis=200
其中-XX:+UseG1GC為開(kāi)啟G1垃圾收集器与学,-Xmx32g 設(shè)計(jì)堆內(nèi)存的最大內(nèi)存為32G,-XX:MaxGCPauseMillis=200設(shè)置GC的最大暫停時(shí)間為200ms嘉抓。如果我們需要調(diào)優(yōu)索守,在內(nèi)存大小一定的情況下,我們只需要修改最大暫停時(shí)間即可抑片。
其次卵佛,G1將新生代,老年代的物理空間劃分取消了敞斋。
這樣我們?cè)僖膊挥脝为?dú)的空間對(duì)每個(gè)代進(jìn)行設(shè)置了截汪,不用擔(dān)心每個(gè)代內(nèi)存是否足夠。
取而代之的是渺尘,G1算法將堆劃分為若干個(gè)區(qū)域(Region)挫鸽,它仍然屬于分代收集器。不過(guò)鸥跟,這些區(qū)域的一部分包含新生代丢郊,新生代的垃圾收集依然采用暫停所有應(yīng)用線程的方式盔沫,將存活對(duì)象拷貝到老年代或者Survivor空間。老年代也分成很多區(qū)域枫匾,G1收集器通過(guò)將對(duì)象從一個(gè)區(qū)域復(fù)制到另外一個(gè)區(qū)域架诞,完成了清理工作。這就意味著干茉,在正常的處理過(guò)程中谴忧,G1完成了堆的壓縮(至少是部分堆的壓縮),這樣也就不會(huì)有cms內(nèi)存碎片問(wèn)題的存在了角虫。
在G1中沾谓,還有一種特殊的區(qū)域,叫Humongous區(qū)域戳鹅。 如果一個(gè)對(duì)象占用的空間超過(guò)了分區(qū)容量50%以上均驶,G1收集器就認(rèn)為這是一個(gè)巨型對(duì)象。這些巨型對(duì)象枫虏,默認(rèn)直接會(huì)被分配在年老代妇穴,但是如果它是一個(gè)短期存在的巨型對(duì)象,就會(huì)對(duì)垃圾收集器造成負(fù)面影響隶债。為了解決這個(gè)問(wèn)題腾它,G1劃分了一個(gè)Humongous區(qū),它用來(lái)專門(mén)存放巨型對(duì)象死讹。如果一個(gè)H區(qū)裝不下一個(gè)巨型對(duì)象瞒滴,那么G1會(huì)尋找連續(xù)的H分區(qū)來(lái)存儲(chǔ)。為了能找到連續(xù)的H區(qū)回俐,有時(shí)候不得不啟動(dòng)Full GC逛腿。
PS:在java 8中稀并,持久代也移動(dòng)到了普通的堆內(nèi)存空間中仅颇,改為元空間。
對(duì)象分配策略
說(shuō)起大對(duì)象的分配碘举,我們不得不談?wù)剬?duì)象的分配策略忘瓦。它分為3個(gè)階段:
- TLAB(Thread Local Allocation Buffer)線程本地分配緩沖區(qū)
- Eden區(qū)中分配
- Humongous區(qū)分配
TLAB為線程本地分配緩沖區(qū),它的目的為了使對(duì)象盡可能快的分配出來(lái)引颈。如果對(duì)象在一個(gè)共享的空間中分配耕皮,我們需要采用一些同步機(jī)制來(lái)管理這些空間內(nèi)的空閑空間指針。在Eden空間中蝙场,每一個(gè)線程都有一個(gè)固定的分區(qū)用于分配對(duì)象凌停,即一個(gè)TLAB。分配對(duì)象時(shí)售滤,線程之間不再需要進(jìn)行任何的同步罚拟。
對(duì)TLAB空間中無(wú)法分配的對(duì)象台诗,JVM會(huì)嘗試在Eden空間中進(jìn)行分配。如果Eden空間無(wú)法容納該對(duì)象赐俗,就只能在老年代中進(jìn)行分配空間拉队。
最后,G1提供了兩種GC模式阻逮,Young GC和Mixed GC粱快,兩種都是Stop The World(STW)的。下面我們將分別介紹一下這2種模式叔扼。
三事哭,G1 Young GC
Young GC主要是對(duì)Eden區(qū)進(jìn)行GC,它在Eden空間耗盡時(shí)會(huì)被觸發(fā)瓜富。在這種情況下慷蠕,Eden空間的數(shù)據(jù)移動(dòng)到Survivor空間中,如果Survivor空間不夠食呻,Eden空間的部分?jǐn)?shù)據(jù)會(huì)直接晉升到年老代空間流炕。Survivor區(qū)的數(shù)據(jù)移動(dòng)到新的Survivor區(qū)中,也有部分?jǐn)?shù)據(jù)晉升到老年代空間中仅胞。最終Eden空間的數(shù)據(jù)為空每辟,GC停止工作,應(yīng)用線程繼續(xù)執(zhí)行干旧。
這時(shí)渠欺,我們需要考慮一個(gè)問(wèn)題,如果僅僅GC 新生代對(duì)象椎眯,我們?nèi)绾握业剿械母鶎?duì)象呢挠将? 老年代的所有對(duì)象都是根么?那這樣掃描下來(lái)會(huì)耗費(fèi)大量的時(shí)間编整。于是舔稀,G1引進(jìn)了RSet的概念。它的全稱是Remembered Set掌测,作用是跟蹤指向某個(gè)heap區(qū)內(nèi)的對(duì)象引用内贮。
在CMS中,也有RSet的概念汞斧,在老年代中有一塊區(qū)域用來(lái)記錄指向新生代的引用夜郁。這是一種point-out,在進(jìn)行Young GC時(shí)粘勒,掃描根時(shí)竞端,僅僅需要掃描這一塊區(qū)域,而不需要掃描整個(gè)老年代庙睡。
但在G1中事富,并沒(méi)有使用point-out剑勾,這是由于一個(gè)分區(qū)太小,分區(qū)數(shù)量太多赵颅,如果是用point-out的話虽另,會(huì)造成大量的掃描浪費(fèi),有些根本不需要GC的分區(qū)引用也掃描了饺谬。于是G1中使用point-in來(lái)解決捂刺。point-in的意思是哪些分區(qū)引用了當(dāng)前分區(qū)中的對(duì)象。這樣募寨,僅僅將這些對(duì)象當(dāng)做根來(lái)掃描就避免了無(wú)效的掃描族展。由于新生代有多個(gè),那么我們需要在新生代之間記錄引用嗎拔鹰?這是不必要的仪缸,原因在于每次GC時(shí),所有新生代都會(huì)被掃描列肢,所以只需要記錄老年代到新生代之間的引用即可恰画。
需要注意的是,如果引用的對(duì)象很多瓷马,賦值器需要對(duì)每個(gè)引用做處理拴还,賦值器開(kāi)銷會(huì)很大,為了解決賦值器開(kāi)銷這個(gè)問(wèn)題欧聘,在G1 中又引入了另外一個(gè)概念片林,卡表(Card Table)。一個(gè)Card Table將一個(gè)分區(qū)在邏輯上劃分為固定大小的連續(xù)區(qū)域怀骤,每個(gè)區(qū)域稱之為卡费封。卡通常較小蒋伦,介于128到512字節(jié)之間弓摘。Card Table通常為字節(jié)數(shù)組,由Card的索引(即數(shù)組下標(biāo))來(lái)標(biāo)識(shí)每個(gè)分區(qū)的空間地址凉敲。默認(rèn)情況下衣盾,每個(gè)卡都未被引用寺旺。當(dāng)一個(gè)地址空間被引用時(shí)爷抓,這個(gè)地址空間對(duì)應(yīng)的數(shù)組索引的值被標(biāo)記為”0″,即標(biāo)記為臟被引用阻塑,此外RSet也將這個(gè)數(shù)組下標(biāo)記錄下來(lái)蓝撇。一般情況下,這個(gè)RSet其實(shí)是一個(gè)Hash Table陈莽,Key是別的Region的起始地址渤昌,Value是一個(gè)集合虽抄,里面的元素是Card Table的Index。
Young GC 階段:
- 階段1:根掃描
靜態(tài)和本地對(duì)象被掃描 - 階段2:更新RS
處理dirty card隊(duì)列更新RS - 階段3:處理RS
檢測(cè)從年輕代指向年老代的對(duì)象 - 階段4:對(duì)象拷貝
拷貝存活的對(duì)象到survivor/old區(qū)域 - 階段5:處理引用隊(duì)列
軟引用独柑,弱引用迈窟,虛引用處理
四,G1 Mix GC
Mix GC不僅進(jìn)行正常的新生代垃圾收集忌栅,同時(shí)也回收部分后臺(tái)掃描線程標(biāo)記的老年代分區(qū)车酣。
它的GC步驟分2步:
- 全局并發(fā)標(biāo)記(global concurrent marking)
- 拷貝存活對(duì)象(evacuation)
在進(jìn)行Mix GC之前,會(huì)先進(jìn)行g(shù)lobal concurrent marking(全局并發(fā)標(biāo)記)索绪。 global concurrent marking的執(zhí)行過(guò)程是怎樣的呢湖员?
在G1 GC中,它主要是為Mixed GC提供標(biāo)記服務(wù)的瑞驱,并不是一次GC過(guò)程的一個(gè)必須環(huán)節(jié)娘摔。global concurrent marking的執(zhí)行過(guò)程分為五個(gè)步驟:
- 初始標(biāo)記(initial mark,STW)
在此階段唤反,G1 GC 對(duì)根進(jìn)行標(biāo)記凳寺。該階段與常規(guī)的 (STW) 年輕代垃圾回收密切相關(guān)。 - 根區(qū)域掃描(root region scan)
G1 GC 在初始標(biāo)記的存活區(qū)掃描對(duì)老年代的引用彤侍,并標(biāo)記被引用的對(duì)象读第。該階段與應(yīng)用程序(非 STW)同時(shí)運(yùn)行,并且只有完成該階段后拥刻,才能開(kāi)始下一次 STW 年輕代垃圾回收怜瞒。 - 并發(fā)標(biāo)記(Concurrent Marking)
G1 GC 在整個(gè)堆中查找可訪問(wèn)的(存活的)對(duì)象。該階段與應(yīng)用程序同時(shí)運(yùn)行般哼,可以被 STW 年輕代垃圾回收中斷 - 最終標(biāo)記(Remark吴汪,STW)
該階段是 STW 回收,幫助完成標(biāo)記周期蒸眠。G1 GC 清空 SATB 緩沖區(qū)漾橙,跟蹤未被訪問(wèn)的存活對(duì)象,并執(zhí)行引用處理楞卡。 - 清除垃圾(Cleanup霜运,STW)
在這個(gè)最后階段,G1 GC 執(zhí)行統(tǒng)計(jì)和 RSet 凈化的 STW 操作蒋腮。在統(tǒng)計(jì)期間淘捡,G1 GC 會(huì)識(shí)別完全空閑的區(qū)域和可供進(jìn)行混合垃圾回收的區(qū)域。清理階段在將空白區(qū)域重置并返回到空閑列表時(shí)為部分并發(fā)池摧。
三色標(biāo)記算法
提到并發(fā)標(biāo)記焦除,我們不得不了解并發(fā)標(biāo)記的三色標(biāo)記算法。它是描述追蹤式回收器的一種有用的方法作彤,利用它可以推演回收器的正確性膘魄。 首先乌逐,我們將對(duì)象分成三種類型的。
- 黑色:根對(duì)象创葡,或者該對(duì)象與它的子對(duì)象都被掃描
- 灰色:對(duì)象本身被掃描,但還沒(méi)掃描完該對(duì)象中的子對(duì)象
- 白色:未被掃描對(duì)象浙踢,掃描完成所有對(duì)象之后,最終為白色的為不可達(dá)對(duì)象灿渴,即垃圾對(duì)象
當(dāng)GC開(kāi)始掃描對(duì)象時(shí)成黄,按照如下圖步驟進(jìn)行對(duì)象的掃描:
根對(duì)象被置為黑色,子對(duì)象被置為灰色逻杖。
繼續(xù)由灰色遍歷,將已掃描了子對(duì)象的對(duì)象置為黑色奋岁。
遍歷了所有可達(dá)的對(duì)象后,所有可達(dá)的對(duì)象都變成了黑色荸百。不可達(dá)的對(duì)象即為白色闻伶,需要被清理。
這看起來(lái)很美好够话,但是如果在標(biāo)記過(guò)程中蓝翰,應(yīng)用程序也在運(yùn)行,那么對(duì)象的指針就有可能改變女嘲。這樣的話畜份,我們就會(huì)遇到一個(gè)問(wèn)題:對(duì)象丟失問(wèn)題
我們看下面一種情況,當(dāng)垃圾收集器掃描到下面情況時(shí):
這時(shí)候應(yīng)用程序執(zhí)行了以下操作:
A.c=C
B.c=null
這樣欣尼,對(duì)象的狀態(tài)圖變成如下情形:
這時(shí)候垃圾收集器再標(biāo)記掃描的時(shí)候就會(huì)下圖成這樣:
很顯然爆雹,此時(shí)C是白色,被認(rèn)為是垃圾需要清理掉愕鼓,顯然這是不合理的钙态。那么我們?nèi)绾伪WC應(yīng)用程序在運(yùn)行的時(shí)候,GC標(biāo)記的對(duì)象不丟失呢菇晃?有如下2中可行的方式:
- 在插入的時(shí)候記錄對(duì)象
- 在刪除的時(shí)候記錄對(duì)象
剛好這對(duì)應(yīng)CMS和G1的2種不同實(shí)現(xiàn)方式:
在CMS采用的是增量更新(Incremental update)册倒,只要在寫(xiě)屏障(write barrier)里發(fā)現(xiàn)要有一個(gè)白對(duì)象的引用被賦值到一個(gè)黑對(duì)象 的字段里,那就把這個(gè)白對(duì)象變成灰色的磺送。即插入的時(shí)候記錄下來(lái)驻子。
在G1中,使用的是STAB(snapshot-at-the-beginning)的方式估灿,刪除的時(shí)候記錄所有的對(duì)象崇呵,它有3個(gè)步驟:
1,在開(kāi)始標(biāo)記的時(shí)候生成一個(gè)快照?qǐng)D標(biāo)記存活對(duì)象
2甲捏,在并發(fā)標(biāo)記的時(shí)候所有被改變的對(duì)象入隊(duì)(在write barrier里把所有舊的引用所指向的對(duì)象都變成非白的)
3演熟,可能存在游離的垃圾,將在下次被收集
這樣司顿,G1到現(xiàn)在可以知道哪些老的分區(qū)可回收垃圾最多芒粹。 當(dāng)全局并發(fā)標(biāo)記完成后,在某個(gè)時(shí)刻大溜,就開(kāi)始了Mix GC化漆。這些垃圾回收被稱作“混合式”是因?yàn)樗麄儾粌H僅進(jìn)行正常的新生代垃圾收集,同時(shí)也回收部分后臺(tái)掃描線程標(biāo)記的分區(qū)钦奋∽疲混合式垃圾收集如下圖:
混合式GC也是采用的復(fù)制的清理策略,當(dāng)GC完成后付材,會(huì)重新釋放空間朦拖。
至此,混合式GC告一段落了厌衔。下一小節(jié)我們講進(jìn)入調(diào)優(yōu)實(shí)踐璧帝。
五,調(diào)優(yōu)實(shí)踐
MaxGCPauseMillis調(diào)優(yōu)
前面介紹過(guò)使用GC的最基本的參數(shù):
-XX:+UseG1GC -Xmx32g -XX:MaxGCPauseMillis=200
前面2個(gè)參數(shù)都好理解富寿,后面這個(gè)MaxGCPauseMillis參數(shù)該怎么配置呢睬隶?這個(gè)參數(shù)從字面的意思上看,就是允許的GC最大的暫停時(shí)間页徐。G1盡量確保每次GC暫停的時(shí)間都在設(shè)置的MaxGCPauseMillis范圍內(nèi)苏潜。 那G1是如何做到最大暫停時(shí)間的呢?這涉及到另一個(gè)概念变勇,CSet(collection set)恤左。它的意思是在一次垃圾收集器中被收集的區(qū)域集合。
- Young GC:選定所有新生代里的region搀绣。通過(guò)控制新生代的region個(gè)數(shù)來(lái)控制young GC的開(kāi)銷赃梧。
- Mixed GC:選定所有新生代里的region,外加根據(jù)global concurrent marking統(tǒng)計(jì)得出收集收益高的若干老年代region豌熄。在用戶指定的開(kāi)銷目標(biāo)范圍內(nèi)盡可能選擇收益高的老年代region授嘀。
在理解了這些后,我們?cè)僭O(shè)置最大暫停時(shí)間就好辦了锣险。 首先蹄皱,我們能容忍的最大暫停時(shí)間是有一個(gè)限度的,我們需要在這個(gè)限度范圍內(nèi)設(shè)置芯肤。但是應(yīng)該設(shè)置的值是多少呢巷折?我們需要在吞吐量跟MaxGCPauseMillis之間做一個(gè)平衡。如果MaxGCPauseMillis設(shè)置的過(guò)小崖咨,那么GC就會(huì)頻繁锻拘,吞吐量就會(huì)下降。如果MaxGCPauseMillis設(shè)置的過(guò)大,應(yīng)用程序暫停時(shí)間就會(huì)變長(zhǎng)署拟。G1的默認(rèn)暫停時(shí)間是200毫秒婉宰,我們可以從這里入手,調(diào)整合適的時(shí)間推穷。
其他調(diào)優(yōu)參數(shù)
-XX:G1HeapRegionSize=n
設(shè)置的 G1 區(qū)域的大小心包。值是 2 的冪,范圍是 1 MB 到 32 MB 之間馒铃。目標(biāo)是根據(jù)最小的 Java 堆大小劃分出約 2048 個(gè)區(qū)域蟹腾。
-XX:ParallelGCThreads=n
設(shè)置 STW 工作線程數(shù)的值。將 n 的值設(shè)置為邏輯處理器的數(shù)量区宇。n 的值與邏輯處理器的數(shù)量相同娃殖,最多為 8。
如果邏輯處理器不止八個(gè)议谷,則將 n 的值設(shè)置為邏輯處理器數(shù)的 5/8 左右炉爆。這適用于大多數(shù)情況,除非是較大的 SPARC 系統(tǒng)柿隙,其中 n 的值可以是邏輯處理器數(shù)的 5/16 左右叶洞。
-XX:ConcGCThreads=n
設(shè)置并行標(biāo)記的線程數(shù)。將 n 設(shè)置為并行垃圾回收線程數(shù) (ParallelGCThreads) 的 1/4 左右禀崖。
-XX:InitiatingHeapOccupancyPercent=45
設(shè)置觸發(fā)標(biāo)記周期的 Java 堆占用率閾值衩辟。默認(rèn)占用率是整個(gè) Java 堆的 45%。
避免使用以下參數(shù):
避免使用 -Xmn 選項(xiàng)或 -XX:NewRatio 等其他相關(guān)選項(xiàng)顯式設(shè)置年輕代大小波附。固定年輕代的大小會(huì)覆蓋暫停時(shí)間目標(biāo)夭禽。
觸發(fā)Full GC
在某些情況下溜哮,G1觸發(fā)了Full GC状答,這時(shí)G1會(huì)退化使用Serial收集器來(lái)完成垃圾的清理工作粉洼,它僅僅使用單線程來(lái)完成GC工作,GC暫停時(shí)間將達(dá)到秒級(jí)別的仅财。整個(gè)應(yīng)用處于假死狀態(tài)狈究,不能處理任何請(qǐng)求,我們的程序當(dāng)然不希望看到這些盏求。那么發(fā)生Full GC的情況有哪些呢抖锥?
- 并發(fā)模式失敗
G1啟動(dòng)標(biāo)記周期,但在Mix GC之前碎罚,老年代就被填滿磅废,這時(shí)候G1會(huì)放棄標(biāo)記周期。這種情形下荆烈,需要增加堆大小拯勉,或者調(diào)整周期(例如增加線程數(shù)-XX:ConcGCThreads等)。
- 晉升失敗或者疏散失敗
G1在進(jìn)行GC的時(shí)候沒(méi)有足夠的內(nèi)存供存活對(duì)象或晉升對(duì)象使用,由此觸發(fā)了Full GC宫峦〔砻保可以在日志中看到(to-space exhausted)或者(to-space overflow)。解決這種問(wèn)題的方式是:
a,增加 -XX:G1ReservePercent 選項(xiàng)的值(并相應(yīng)增加總的堆大卸范簟)山卦,為“目標(biāo)空間”增加預(yù)留內(nèi)存量鞋邑。
b,通過(guò)減少 -XX:InitiatingHeapOccupancyPercent 提前啟動(dòng)標(biāo)記周期诵次。
c,也可以通過(guò)增加 -XX:ConcGCThreads 選項(xiàng)的值來(lái)增加并行標(biāo)記線程的數(shù)目。
- 巨型對(duì)象分配失敗
當(dāng)巨型對(duì)象找不到合適的空間進(jìn)行分配時(shí)枚碗,就會(huì)啟動(dòng)Full GC逾一,來(lái)釋放空間。這種情況下肮雨,應(yīng)該避免分配大量的巨型對(duì)象遵堵,增加內(nèi)存或者增大-XX:G1HeapRegionSize,使巨型對(duì)象不再是巨型對(duì)象怨规。
由于篇幅有限陌宿,G1還有很多調(diào)優(yōu)實(shí)踐,在此就不一一列出了波丰,大家在平常的實(shí)踐中可以慢慢探索壳坪。最后,期待java 9能正式發(fā)布掰烟,默認(rèn)使用G1為垃圾收集器的java性能會(huì)不會(huì)又提高呢爽蝴?
附錄: