hbase gc調(diào)優(yōu)(CMS與G1)
一:hbase gc調(diào)優(yōu)目的:
在HBase中,有兩個(gè)在內(nèi)存中的結(jié)構(gòu)消費(fèi)了絕大多數(shù)的heap空間。BlockCache緩存讀操作的HFileblock,Memstore緩存近期的寫操作,基于HBase是一個(gè)響應(yīng)時(shí)間敏感,并且需要對GC時(shí)間可控的應(yīng)用的出發(fā)點(diǎn)。
二:hbase的gc調(diào)優(yōu)本文通過CMS與g1兩種算法進(jìn)行設(shè)置參考。
(需要大致了解兩種垃圾收集器的概念及不同)
三:幾個(gè)基本概念:
- Full GC == Major GC指的是對老年代/永久代的stop the world的GC
- Full GC的次數(shù) = 老年代GC時(shí) stop the world的次數(shù)
- Full GC的時(shí)間 = 老年代GC時(shí) stop the world的總時(shí)間
- CMS 不等于Full GC,我們可以看到CMS分為多個(gè)階段,只有stop the world的階段被計(jì)算到了Full GC的次數(shù)和時(shí)間,而和業(yè)務(wù)線程并發(fā)的GC的次數(shù)和時(shí)間則不被認(rèn)為是Full GC
- Full GC本身不會先進(jìn)行Minor GC,我們可以配置,讓Full GC之前先進(jìn)行一次Minor GC,因?yàn)槔夏甏芏鄬ο蠖紩玫叫律膶ο?,先進(jìn)行一次Minor GC可以提高老年代GC的速度。比如老年代使用CMS時(shí),設(shè)置CMSScavengeBeforeRemark優(yōu)化,讓CMS remark之前先進(jìn)行一次Minor GC。
四:CMS調(diào)優(yōu):
1.CMS收集器在老年代內(nèi)存回收中執(zhí)行的階段說明:
(1) 初始標(biāo)記 (Initial Mark) (Stop the World Event,所有應(yīng)用線程暫停):在老年代(old generation)中的對象, 如果從年輕代(young generation)中能訪問到, 則被 “標(biāo)記,marked” 為可達(dá)的(reachable).對象在舊一代“標(biāo)志”可以包括這些對象可能可以從年輕一代。暫停時(shí)間一般持續(xù)時(shí)間較短,相對小的收集暫停時(shí)間.
(2) 并發(fā)標(biāo)記 (Concurrent Marking):在Java應(yīng)用程序線程運(yùn)行的同時(shí)遍歷老年代(tenured generation)的可達(dá)對象圖。掃描從被標(biāo)記的對象開始,直到遍歷完從root可達(dá)的所有對象. 調(diào)整器(mutators)在并發(fā)階段的2、3、5階段執(zhí)行,在這些階段中新分配的所有對象(包括被提升的對象)都立刻標(biāo)記為存活狀態(tài).
(3) 再次標(biāo)記(Remark):(Stop the World Event, 所有應(yīng)用線程暫停) 查找在并發(fā)標(biāo)記階段漏過的對象,這些對象是在并發(fā)收集器完成對象跟蹤之后由應(yīng)用線程更新的.
(4) 并發(fā)清理(Concurrent Sweep):回收在標(biāo)記階段(marking phases)確定為不可及的對象. 死對象的回收將此對象占用的空間增加到一個(gè)空閑列表(free list),供以后的分配使用。死對象的合并可能在此時(shí)發(fā)生. 請注意,存活的對象并沒有被移動.
(5) 重置(Resetting):清理數(shù)據(jù)結(jié)構(gòu),為下一個(gè)并發(fā)收集做準(zhǔn)備.
2.CMS的失效模式(CMS Failure)
并發(fā)模式失敗
第一種失敗的模式,是簡單的并發(fā)模式失敗。最好的一個(gè)例子:假設(shè)有一個(gè)8GB堆,已經(jīng)使用了7GB。當(dāng)CMS的收集開始第一階段,它歡快的隆隆的做著并發(fā)標(biāo)記。與此同時(shí),有更多的數(shù)據(jù)被分配到老年代。如果老年代增長的速度太快,在CMS完成第一階段標(biāo)記工作之前就填滿了全部老年代。這時(shí)候因?yàn)闆]有自由空間,CMS就無法工作!CMS必須放棄并行工作,并回落到停止世界(stop-the-world)單線程復(fù)制收集算法。此算法開始搬遷堆,檢查所有活動對象,并釋放了所有死角。長時(shí)間的停頓后,該程序可能會繼續(xù)。
但我們可以很容易的通過調(diào)整JVM參數(shù)避免并發(fā)模式失?。何覀冎恍枰膭頒MS提前開始工作!設(shè)置-XX:CMSInitiatingOccupancyFraction = N,其中N是堆在開始收集過程中的百分比。HBase仔細(xì)的計(jì)算了內(nèi)存使用,以保持其只使用60%的堆空間,所以我們通常將此值設(shè)置為大約70。(譯者注:同時(shí)也可以考慮Old區(qū)的30%要比Young區(qū)大,這樣即使Young區(qū)在CMS之前全部搬遷到Old區(qū)也不會把Old區(qū)填滿)
碎片導(dǎo)致的CMS失敗
這種故障模式是多一點(diǎn)復(fù)雜?;叵胍幌拢珻MS收集不搬遷對象,而是簡單地跟蹤所有堆的自由空間,而且自有空間是分開的。作為一個(gè)思想實(shí)驗(yàn),想象我撥出1億個(gè)對象,每個(gè)1KB,這正是1GB堆的總用量為1GB。然后,我釋放所有奇數(shù)對象,所以我有500MB的自有空間,然而自由空間都是1KB的塊。如果我需要分配一個(gè)2KB的對象,盡管我表面上有500MB免費(fèi)空間,依然會無處可放。這就是所謂的內(nèi)存碎片。因?yàn)镃MS不搬遷對象,不管如何讓CMS提前啟動,都不可以解決這個(gè)問題!發(fā)生此問題時(shí),CM再次回落到復(fù)制收集器,該方法能夠壓縮所有的對象并釋放。
3.CMS收集器參數(shù)設(shè)置參考:
-Xmx64g -Xms64g -Xmn2g -Xss256k
-XX:MaxPermSize=256m -XX:SurvivorRatio=2
-XX:+UseConcMarkSweepGC -XX:+UseParNewGC
-XX:+CMSParallelRemarkEnabled -XX:MaxTenuringThreshold=15 -XX:+UseCMSCompactAtFullCollection -XX:+UseCMSInitiatingOccupancyOnly
-XX:CMSInitiatingOccupancyFraction=75 -XX:-DisableExplicitGC
-XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintHeapAtGC -Xloggc:/usr/local/deploy/logs/gc.log
4.參數(shù)說明:
-Xmx 分配給JVM最大堆內(nèi)存
-Xms 分配給JVM初始堆內(nèi)存
-Xmn 分配給young區(qū)的內(nèi)存大小
-Xss 分配給每個(gè)線程的堆棧大小
-XX:MaxPermSize 分配給持久代的大小
-XX:SurvivorRatio 表示young區(qū)中Eden與Survivor區(qū)的內(nèi)存大小比例,默認(rèn)為8.
-XX:+UseConcMarkSweepGC 該標(biāo)志首先是激活CMS收集器。默認(rèn)HotSpot JVM使用的是并行收集器。
-XX:+UseParNewGC 當(dāng)使用CMS收集器時(shí),該標(biāo)志激活年輕代使用多線程并行執(zhí)行垃圾回收。這令人很驚訝,我們不能簡單在并行收集器中重用-XX:UserParNewGC標(biāo)志,因?yàn)楦拍钌夏贻p代用的算法是一樣的。然而,對于CMS收集器,年輕代GC算法和老年代GC算法是不同的,因此年輕代GC有兩種不同的實(shí)現(xiàn),并且是兩個(gè)不同的標(biāo)志
-XX:+CMSParallelRemarkEnabled 當(dāng)該標(biāo)志被啟用時(shí),并發(fā)的CMS階段將以多線程執(zhí)行(因此,多個(gè)GC線程會與所有的應(yīng)用程序線程并行工作)。該標(biāo)志已經(jīng)默認(rèn)開啟,如果順序執(zhí)行更好,這取決于所使用的硬件,多線程執(zhí)行可以通過-XX:-CMSConcurremntMTEnabled禁用。
-XX:MaxTenuringThreshold 如果設(shè)置為0的話,則年輕代對象不經(jīng)過Survivor區(qū),直接進(jìn)入年老代.對于年老代比較多的應(yīng)用,可以提高效率.如果將此值設(shè)置為一個(gè)較大值,則年輕代對象會在Survivor區(qū)進(jìn)行多次復(fù)制,這樣可以增加對象再年輕代的存活 時(shí)間,增加在年輕代即被回收的概率,該參數(shù)只有在串行GC時(shí)才有效.
-XX:+UseCMSCompactAtFullCollectionCMS 是不會移動內(nèi)存的, 因此, 這個(gè)非常容易產(chǎn)生碎片, 導(dǎo)致內(nèi)存不夠用, 因此, 內(nèi)存的壓縮這個(gè)時(shí)候就會被啟用。 增加這個(gè)參數(shù)是個(gè)好習(xí)慣??赡軙绊懶阅?但是可以消除碎片
-XX:+UseCMSInitiatingOccupancyOnly 禁止hostspot自行觸發(fā)CMS GC
-XX:CMSInitiatingOccupancyFraction 使用cms作為垃圾回收使用70%后開始CMS收集
-XX:-DisableExplicitGC 關(guān)閉System.gc()
五:g1垃圾收集器:
1.概念介紹:
G1 (Garbage-First)是一款面向服務(wù)器的垃圾收集器,主要針對配備多顆處理器及大容量內(nèi)存的機(jī)器. 以極高概率滿足GC停頓時(shí)間要求的同時(shí),還具備高吞吐量性能特征. 在Oracle JDK 7 update 4 及以上版本中得到完全支持, 專為以下應(yīng)用程序設(shè)計(jì):
可以像CMS收集器一樣,GC操作與應(yīng)用的線程一起并發(fā)執(zhí)行
緊湊的空閑內(nèi)存區(qū)間且沒有很長的GC停頓時(shí)間.
需要可預(yù)測的GC暫停耗時(shí).
不想犧牲太多吞吐量性能.
啟動后不需要請求更大的Java堆.
G1的長期目標(biāo)是取代CMS(Concurrent Mark-Sweep Collector, 并發(fā)標(biāo)記-清除). 因?yàn)樘匦缘牟煌笹1成為比CMS更好的解決方案. 一個(gè)區(qū)別是,G1是一款壓縮型的收集器.G1通過有效的壓縮完全避免了對細(xì)微空閑內(nèi)存空間的分配,不用依賴于regions,這不僅大大簡化了收集器,而且還消除了潛在的內(nèi)存碎片問題。除壓縮以外,G1的垃圾收集停頓也比CMS容易估計(jì),也允許用戶自定義所希望的停頓參數(shù)(pause targets)
2.G1收集器在老年代堆內(nèi)存中執(zhí)行階段.(注意有些階段也是年輕代垃圾收集的一部分):
(1) 初始標(biāo)記(Initial Mark):(Stop the World Event,所有應(yīng)用線程暫停) 此時(shí)會有一次 stop the world(STW)暫停事件. 在G1中, 這附加在(piggybacked on)一次正常的年輕代GC. 標(biāo)記可能有引用指向老年代對象的survivor區(qū)(根regions).
(2) 掃描根區(qū)域(Root Region Scanning):掃描 survivor 區(qū)中引用到老年代的引用. 這個(gè)階段應(yīng)用程序的線程會繼續(xù)運(yùn)行. 在年輕代GC可能發(fā)生之前此階段必須完成.
(3) 并發(fā)標(biāo)記(Concurrent Marking):在整個(gè)堆中查找活著的對象. 此階段應(yīng)用程序的線程正在運(yùn)行. 此階段可以被年輕代GC打斷(interrupted).
(4) 再次標(biāo)記(Remark):(Stop the World Event,所有應(yīng)用線程暫停) 完成堆內(nèi)存中存活對象的標(biāo)記. 使用一個(gè)叫做 snapshot-at-the-beginning(SATB, 起始快照)的算法, 該算法比CMS所使用的算法要快速的多.
(5) 清理(Cleanup):(Stop the World Event,所有應(yīng)用線程暫停,并發(fā)執(zhí)行)
在存活對象和完全空閑的區(qū)域上執(zhí)行統(tǒng)計(jì)(accounting). (Stop the world)
擦寫 Remembered Sets. (Stop the world)
重置空heap區(qū)并將他們返還給空閑列表(free list). (Concurrent, 并發(fā))
(*) 拷貝(Copying) (Stop the World Event,所有應(yīng)用線程暫停) 產(chǎn)生STW事件來轉(zhuǎn)移或拷貝存活的對象到新的未使用的heap區(qū)(new unused regions). 只在年輕代發(fā)生時(shí)日志會記錄為
[GC pause (young)]. 如果在年輕代和老年代一起執(zhí)行則會被日志記錄為[GC Pause (mixed)].
3.G1收集器參數(shù)配置參考:
GC參數(shù):
-Xmx50g
-XX:+UseG1GC
-XX:+UnlockExperimentalVMOptions
-XX:MaxGCPauseMillis=100
-XX:InitiatingHeapOccupancyPercent=65
-XX:+ParallelRefProcEnabled
-XX:MaxTenuringThreshold=1
-XX:G1HeapRegionSize=16m
GC日志打印添加參數(shù):
-verbose:gc
-XX:+PrintGC
-XX:+PrintGCDetails
-XX:+PrintGCApplicationStoppedTime
-XX:+PrintHeapAtGC
-XX:+PrintGCDateStamps
-XX:+PrintAdaptiveSizePolicy
-XX:+PrintTenuringDistribution
-XX:+PrintSafepointStatistics
-XX:PrintSafepointStatisticsCount=1
-XX:PrintFLSStatistics=1
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=/var/log/oom/hbase
-Xloggc:/var/log/hbase-server-gc.log
4.G1收集器參數(shù)說明:
-XX:+UseG1GC 使用 G1 (Garbage First) 垃圾收集器
-XX:MaxGCPauseMillis=n 設(shè)置最大GC停頓時(shí)間(GC pause time)指標(biāo)(target). 這是一個(gè)軟性指標(biāo)(soft goal), JVM 會盡量去達(dá)成這個(gè)目標(biāo).
-XX:InitiatingHeapOccupancyPercent=n 啟動并發(fā)GC周期時(shí)的堆內(nèi)存占用百分比. G1之類的垃圾收集器用它來觸發(fā)并發(fā)GC周期,基于整個(gè)堆的使用率,而不只是某一代內(nèi)存的使用比. 值為 0 則表示"一直執(zhí)行GC循環(huán)". 默認(rèn)值為 45.
-XX:NewRatio=n 新生代與老生代(new/old generation)的大小比例(Ratio). 默認(rèn)值為 2.
-XX:SurvivorRatio=n eden/survivor 空間大小的比例(Ratio). 默認(rèn)值為 8.
-XX:MaxTenuringThreshold=n 提升年老代的最大臨界值(tenuring threshold). 默認(rèn)值為 15.
-XX:ParallelGCThreads=n 設(shè)置垃圾收集器在并行階段使用的線程數(shù),默認(rèn)值隨JVM運(yùn)行的平臺不同而不同.
-XX:ConcGCThreads=n 并發(fā)垃圾收集器使用的線程數(shù)量. 默認(rèn)值隨JVM運(yùn)行的平臺不同而不同.
-XX:G1ReservePercent=n 設(shè)置堆內(nèi)存保留為假天花板的總量,以降低提升失敗的可能性. 默認(rèn)值是 10.
-XX:G1HeapRegionSize=n 使用G1時(shí)Java堆會被分為大小統(tǒng)一的的區(qū)(region)。此參數(shù)可以指定每個(gè)heap區(qū)的大小. 默認(rèn)值將根據(jù) heap size 算出最優(yōu)解. 最小值為 1Mb, 最大值為 32Mb.