2019/4/2 星期二
深入研究java gc
引出問(wèn)題和小結(jié)!
應(yīng)該是全網(wǎng)最全的JVM知識(shí)點(diǎn)總結(jié) https://www.toutiao.com/i6717184983829578254/
//此鏈接很重要 推薦收藏
你解釋一下什么是JVM?什么是JDK?什么是JRE?我懵了
https://www.toutiao.com/i6714156440199627276/
小結(jié):
1、為什么使用CMS gc回收算法?
//答:
因?yàn)樵贑MS gc算法執(zhí)行的6個(gè)步驟中,只有在第一步(初始標(biāo)記(STW Initial Mark))和第四步重新標(biāo)記階段(STW REMARK)才會(huì)暫停整個(gè)應(yīng)用,這樣對(duì)應(yīng)用程序所帶來(lái)的影響非常小,缺點(diǎn)是產(chǎn)生內(nèi)存碎片過(guò)多
2、那CMS GC策略如何導(dǎo)致內(nèi)存碎片過(guò)多?
//答:是因?yàn)榈诙讲l(fā)標(biāo)記(concurrent marking)與回收線程會(huì)與應(yīng)用程序爭(zhēng)搶CPU資源,容易產(chǎn)生內(nèi)存碎片,其二:CMS算法在標(biāo)記清理之后并沒(méi)有重新壓縮分配存活對(duì)象,因此整個(gè)老生代會(huì)產(chǎn)生很多的內(nèi)存碎片。
3、那為什么CMS gc策略會(huì)耗時(shí)比較長(zhǎng)呢?
//答:
‘stop-the-world’暫停時(shí)間也很短暫,耗時(shí)較長(zhǎng)的(第二步并發(fā)標(biāo)記)標(biāo)記和(第三步并發(fā)預(yù)清理)清理都是并發(fā)執(zhí)行的。
內(nèi)存碎片過(guò)多如何觸發(fā)Full GC?
//答:
CMS并不是很完美,它會(huì)在兩種場(chǎng)景下產(chǎn)生嚴(yán)重的Full GC(Concurrent Failure(并發(fā)失敗),Promotion Failure (促銷失?。?br/>具體見(jiàn):老年代 CMS gc回收算法 對(duì)hbase的影響 https://blog.51cto.com/12445535/2373206
HBase在演進(jìn)的道路上又如何不斷優(yōu)化CMS GC?
具體內(nèi)容見(jiàn)下詳細(xì)介紹:
題外話:什么是java程序的執(zhí)行流程;java運(yùn)行時(shí)數(shù)據(jù)區(qū);java的內(nèi)存管理 見(jiàn)如下圖:
java程序執(zhí)行流程:
java運(yùn)行時(shí)數(shù)據(jù)區(qū):
java的內(nèi)存管理:
在我們(運(yùn)行時(shí)數(shù)據(jù)區(qū))之中,內(nèi)存的分配一共有五塊:
1、堆內(nèi)存(Heap):保存真正的程序的數(shù)據(jù)的部分;
2、&&&棧內(nèi)存(Stack):保存堆內(nèi)存地址、還保存基本數(shù)據(jù)、方法的執(zhí)行;(所有的數(shù)據(jù)都在棧內(nèi)存之中)
3、方法區(qū):保存所有方法的具體的操作,該區(qū)域?qū)儆诠蚕恚?br/>4、程序計(jì)數(shù)器:這是一塊很小的內(nèi)存,小到幾乎可以忽略的地步,只是做一個(gè)程序執(zhí)行順序的記錄,只是為了標(biāo)記我們下一步要執(zhí)行的代碼的順序號(hào);
5、本地方法棧:該棧之中所保存的都是操作系統(tǒng)的原生函數(shù)。
我們關(guān)心的主要是堆內(nèi)存、棧內(nèi)存、方法區(qū)
在整個(gè)的JVM內(nèi)存組成過(guò)程之中,(棧內(nèi)存)是一個(gè)非常重要的概念,因?yàn)樵谠搩?nèi)存之中,他需要保存的數(shù)據(jù)是一組內(nèi)容,
因?yàn)樗械姆椒ㄔ谶M(jìn)行遞歸調(diào)用的時(shí)候都會(huì)采用棧的模式。觀察遞歸問(wèn)題中滿棧的原因取決于服務(wù)器內(nèi)存的大小。
內(nèi)存操作有關(guān)的兩類異常
stackOverFlowError(棧溢出):如果請(qǐng)求的棧的深度過(guò)大,虛擬機(jī)可能會(huì)拋處。
OutOfMemoryError(內(nèi)存溢出):如果虛擬機(jī)的實(shí)現(xiàn)中允許、虛擬機(jī)棧動(dòng)態(tài)擴(kuò)展,當(dāng)內(nèi)存不足以擴(kuò)展棧的時(shí)候,會(huì)拋出?!緝?nèi)存被沾滿,更多情況下表示堆也分配不了了】
實(shí)際上上面只是觀察到了兩類可能出現(xiàn)錯(cuò)誤的代碼,但是并不是意味著棧中只能夠保存一下基本的信息,實(shí)際上棧里面保存同樣是一組的數(shù)據(jù)。
總結(jié):
1、造成stackOverFlowError(棧溢出)OutOfMemoryError(內(nèi)存溢出)的原因是;
2、在JVM棧內(nèi)存中保存有棧幁的概念,所有的棧內(nèi)存采用先進(jìn)后出的數(shù)據(jù)結(jié)構(gòu)來(lái)進(jìn)行我們的存儲(chǔ)。
首先需要了解一下什么是java的堆內(nèi)內(nèi)存劃分
在實(shí)際情況下:java 堆內(nèi)存劃分分為了(jdk1.8以前和jdk1.8之后)【對(duì)于這2者的區(qū)別,我們后面介紹】
jvm堆內(nèi)存劃分(jdk1.8以前):
jvm堆內(nèi)存劃分(jdk1.8之后):
java堆內(nèi)存模型
java的垃圾收集主要指的是java堆內(nèi)存空間,那么在每一次執(zhí)行GC的時(shí)候需要區(qū)分出那些堆內(nèi)存空間需要被回收,那些不應(yīng)該被回收。 所以為了整個(gè)的回收處理方便,JVM將堆內(nèi)存分為如下的幾個(gè)組成部分。而這幾個(gè)組成部分你還需要去考慮JDK的版本,現(xiàn)在的JVM內(nèi)存劃分就必須考慮JDK1.8以前和JDK1.8之后的問(wèn)題了。
如果簡(jiǎn)化點(diǎn)來(lái)理解的話:
1、新生代:那些剛剛創(chuàng)建的對(duì)象,剛剛創(chuàng)建的對(duì)象有可能會(huì)存在有許多垃圾對(duì)象,那么這些對(duì)象應(yīng)該是被優(yōu)先回收的;
2、老年代:老不死的那類對(duì)象,經(jīng)過(guò)了很多次的清理之后你發(fā)現(xiàn)該對(duì)象依然有用,
3、永久代:intern()方法進(jìn)行入池的對(duì)象實(shí)際上就在永久代中,永久代不會(huì)被回收。因?yàn)槠浔旧韺儆谝粋€(gè)bug性的存在(也就是jdk崩潰了,死了永久代CIA能消失),所以在jdk1.8之后,將其更換為元空間(就是電腦的直接內(nèi)存)。
舉個(gè)例子:我電腦有100G內(nèi)存,80G給了堆內(nèi)存,那剩下的20G就可以給元空間。
在整個(gè)內(nèi)存的組成過(guò)程之中,每一代的內(nèi)存空間都會(huì)有一個(gè)伸縮區(qū),那么該區(qū)域就可以由JVM根據(jù)空間的使用情況,動(dòng)態(tài)擴(kuò)充。
當(dāng)我們適當(dāng)合理的設(shè)置了伸縮區(qū)的內(nèi)存大小之后,那么就可以得到良好的性能提升。也就是說(shuō)最容易的性能提升就是改變伸縮區(qū)的內(nèi)存大小。
首先什么是java gc 、java對(duì)象創(chuàng)建流程
java對(duì)象創(chuàng)建流程如圖:
1、大多數(shù)內(nèi)存對(duì)象要么生存周期比較短,很快就會(huì)沒(méi)人引用,比如處理RPC請(qǐng)求的buffer可能只會(huì)生存幾微秒;
2、要么生存周期比較長(zhǎng),比如Block Cache中的熱點(diǎn)Block,可能就會(huì)生存幾分鐘,甚至更長(zhǎng)時(shí)間。
3、基于這樣的事實(shí),JVM將整個(gè)堆內(nèi)存分為兩個(gè)部分:新生代(young generation)和老生代(tenured generation),除此之外,JVM還有一個(gè)非堆內(nèi)存區(qū)-Perm區(qū),主要存放class信息以及其他meta元信息,
4、其中Young區(qū)又分為Eden區(qū)和兩個(gè)Survivor 區(qū):S0和S1。
5、一個(gè)內(nèi)存對(duì)象在創(chuàng)建之后,首先會(huì)為其在新生代申請(qǐng)一塊內(nèi)存空間,如果這個(gè)對(duì)象在新生代存活了很長(zhǎng)時(shí)間,會(huì)將其遷移到老生代。
6、在大多數(shù)對(duì)延遲敏感的業(yè)務(wù)場(chǎng)景下(比如HBase),建議使用如下JVM參數(shù),-XX:+UseParNewGC和XX:+UseConcMarkSweepGC,其中前者表示對(duì)新生代執(zhí)行并行的垃圾回收機(jī)制,而后者表示對(duì)老生代執(zhí)行并行標(biāo)記-清除垃圾回收機(jī)制。
7、可見(jiàn),JVM允許針對(duì)不同內(nèi)存區(qū)執(zhí)行不同的GC策略。
//在 cdh中默認(rèn)是這樣設(shè)置的
-XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFraction=70 -XX:+CMSParallelRemarkEnabled
接下來(lái)重點(diǎn)先討論一下年輕代
年輕代GC實(shí)現(xiàn)復(fù)制算法:(年輕代GC策略 – Parallel New Collector)
1、對(duì)象初始化之后會(huì)被放入Young區(qū),更具體的話應(yīng)該是Eden區(qū),當(dāng)Eden區(qū)滿了之后,會(huì)進(jìn)行一次GC。
2、GC算法會(huì)檢查所有對(duì)象的引用情況,如果某個(gè)對(duì)象還有被引用,表示該對(duì)象存活。
3、檢查完成之后,會(huì)將這些存活的對(duì)象移到S0區(qū),并且回收整個(gè)Eden區(qū)空間,稱為一次Minor GC;
4、接著新對(duì)象進(jìn)來(lái),又會(huì)放入Eden區(qū),滿了之后會(huì)檢查S0和Eden區(qū)存活的對(duì)象,將所有存活的對(duì)象移到S1區(qū),再回收整個(gè)S0和Eden區(qū)空間;
5、很容易理解,S0和S1兩個(gè)區(qū)總會(huì)有一個(gè)區(qū)是預(yù)留給下次存放存活對(duì)象用的。
這種算法稱為復(fù)制算法,對(duì)于這種算法,有兩點(diǎn)需要關(guān)注:
提高了解篇
年輕代優(yōu)化算法
年輕代內(nèi)存調(diào)整參數(shù)(重要):
接下來(lái)深度研究老年代
什么是老年代 和老年代的full gc:
老生代GC策略 – Concurrent Mark-Sweep(CMS算法)
什么是CMS 為什么CMS?
1、每次執(zhí)行Minor GC之后,都會(huì)有部分生命周期較長(zhǎng)的對(duì)象被移入老生代,一段時(shí)間之后,老生代空間也會(huì)被占滿。
2、此時(shí)就需要針對(duì)老生代空間執(zhí)行GC操作,此處我們介紹Concurrent Mark-Sweep(CMS)算法。
CMS算法整個(gè)流程分為6個(gè)階段,其中部分階段會(huì)執(zhí)行 ‘stop-the-world’ 暫停,部分階段會(huì)和應(yīng)用線程一起并發(fā)執(zhí)行:
如圖:
老年代執(zhí)行CMS過(guò)程:
相應(yīng)的,對(duì)于CMS算法,也需要關(guān)注兩點(diǎn):
提高篇
老年代標(biāo)記清除算法:
老年代標(biāo)記壓縮算法:
老年代內(nèi)存調(diào)整參數(shù):
永久代調(diào)整參數(shù):
元空間調(diào)整參數(shù):
可用gc方式小結(jié):
年輕代串行GC(copy)
年輕代并行回收GC
年輕代并行GC
老年代串行GC
老年代并行GC
常用gc策略:
GC調(diào)整策略、
收集器參數(shù)設(shè)置
G1收集器介紹:
參考鏈接:
http://hbasefly.com/2016/05/21/hbase-gc-1/
另外有需要云服務(wù)器可以了解下創(chuàng)新互聯(lián)scvps.cn,海內(nèi)外云服務(wù)器15元起步,三天無(wú)理由+7*72小時(shí)售后在線,公司持有idc許可證,提供“云服務(wù)器、裸金屬服務(wù)器、高防服務(wù)器、香港服務(wù)器、美國(guó)服務(wù)器、虛擬主機(jī)、免備案服務(wù)器”等云主機(jī)租用服務(wù)以及企業(yè)上云的綜合解決方案,具有“安全穩(wěn)定、簡(jiǎn)單易用、服務(wù)可用性高、性價(jià)比高”等特點(diǎn)與優(yōu)勢(shì),專為企業(yè)上云打造定制,能夠滿足用戶豐富、多元化的應(yīng)用場(chǎng)景需求。