JVM 核心知識點(diǎn)

想要提高程序員自身的內(nèi)功心法無非就是: 數(shù)據(jù)結(jié)構(gòu)跟算法 + 操作系統(tǒng) + 網(wǎng)絡(luò) 鲫趁,而所有的Java代碼都是在JVM上運(yùn)行的,了解了JVM好處就是:

寫出更好更健壯的代碼牲阁。

提高Java的性能赚爵,排除問題。

面試必問 疙描,要對知識有一定的深度 炉峰。

1、簡述JVM 內(nèi)存模型

從宏觀上來說JVM 內(nèi)存區(qū)域 分為三部分 線程共享區(qū)域 疼阔、 線程私有區(qū)域 瓜客、 直接內(nèi)存區(qū)域 。

1.1竿开、線程共享區(qū)域

1.1.1、堆區(qū)

堆區(qū)Heap是JVM中最大的一塊內(nèi)存區(qū)域玻熙,基本上所有的對象實(shí)例都是在堆上分配空間否彩。堆區(qū)細(xì)分為 年輕代老年代 ,其中年輕代又分為Eden嗦随、S0列荔、S1 三個(gè)部分敬尺,他們默認(rèn)的比例是 8:1:1的大小。

1.1.1元空間

方法區(qū):

在 《Java虛擬機(jī)規(guī)范》中只是規(guī)定了有 方法區(qū) 這么個(gè) 概念 跟它的 作用 贴浙。 HotSpot 在JDK8之前 搞了個(gè) 永久代 把這個(gè)概念實(shí)現(xiàn)了砂吞。用來主要存儲類信息、常量池崎溃、靜態(tài)變量蜻直、JIT編譯后的代碼等數(shù)據(jù)。

PermGen(永久代)中類的元數(shù)據(jù)信息在每次 FullGC 的時(shí)候可能會(huì)被收集袁串,但成績很難令人滿意概而。而且為PermGen分配多大的空間因?yàn)榇鎯ι鲜龆喾N數(shù)據(jù)很難確定大小。因此官方在JDK8提出移除永久代囱修。

官方解釋移除永久代:

This is part of the JRockit and Hotspot convergence effort. JRockit customers do not need to configure the permanent generation (since JRockit does not have a permanent generation) and are accustomed to not configuring the permanent generation.

即:移除永久代是為融合HotSpot JVM與 JRockit VM而做出的努力赎瑰,因?yàn)镴Rockit沒有永久代,不需要配置永久代破镰。

元空間:

在Java中用 永久代 來存儲類信息餐曼,常量,靜態(tài)變量等數(shù)據(jù)不是好辦法鲜漩,因?yàn)檫@樣很容易造成內(nèi)存溢出源譬。同時(shí)對永久代的性能調(diào)優(yōu)也很困難,因此在JDK8中 把 永久代 去除了宇整,引入了元空間 metaspace 瓶佳,原先的class、field等變量放入到metaspace鳞青。

總結(jié):

元空間的本質(zhì)和永久代類似霸饲,都是對JVM規(guī)范中方法區(qū)的實(shí)現(xiàn) 。不過元空間與永久代之間最大的區(qū)別在于: 元空間并不在虛擬機(jī)中臂拓,而是使用本地內(nèi)存 厚脉。因此,默認(rèn)情況下胶惰,元空間的大小僅受本地內(nèi)存限制傻工,但可以通過參數(shù)來指定元空間的大小。

1.2孵滞、直接內(nèi)存區(qū)域

直接內(nèi)存:

一般使用 Native 函數(shù)操作C++代碼來實(shí)現(xiàn)直接分配堆外內(nèi)存中捆,不是虛擬機(jī)運(yùn)行時(shí)數(shù)據(jù)區(qū)的一部分,也不是Java虛擬機(jī)規(guī)范中定義的內(nèi)存區(qū)域坊饶。這塊內(nèi)存不受Java堆空間大小的限制泄伪,但是受本機(jī)總內(nèi)存大小限制所以也會(huì)出現(xiàn)OOM異常。分配空間后 避免了在Java堆區(qū)跟Native堆中來回復(fù)制數(shù)據(jù) 匿级,可以有效提高讀寫效率蟋滴, 但它的創(chuàng)建染厅、銷毀卻比普通Buffer慢

PS: 如果使用了 NIO 津函,本地內(nèi)存區(qū)域會(huì)被頻繁地使用肖粮,此時(shí) jvm內(nèi)存 ≈ 方法區(qū) + 堆 + 棧+ 直接內(nèi)存

1.3、線程私有區(qū)域

程序計(jì)數(shù)器尔苦、虛擬機(jī)棧涩馆、本地方法棧跟線程的聲明周期是一樣的。

1.3.1蕉堰、程序計(jì)數(shù)器

課堂上比如你正在看小說《誅仙》凌净,看到1412章節(jié)時(shí),老師喊你回答問題屋讶,這個(gè)時(shí)候你肯定要先應(yīng)付老師的問題冰寻,回答完畢后繼續(xù)接著看,這個(gè)時(shí)候你可以用書簽也可以憑借記憶記住自己在看的位置皿渗,通過這樣實(shí)現(xiàn)繼續(xù)閱讀斩芭。

落實(shí)到代碼運(yùn)行時(shí)候同樣道理, 程序計(jì)數(shù)器 用于記錄當(dāng)前線程下虛擬機(jī)正在執(zhí)行的字節(jié)碼的指令地址乐疆。它具有如下特性:

線程私有

多線程情況下划乖,在同一時(shí)刻所以為了讓線程切換后依然能恢復(fù)到原位,每條線程都需要有各自獨(dú)立的程序計(jì)數(shù)器挤土。

沒有規(guī)定OutOfMemoryError

程序計(jì)數(shù)器存儲的是字節(jié)碼文件的行號琴庵,而這個(gè)范圍是可知曉的,在一開始分配內(nèi)存時(shí)就可以分配一個(gè)絕對不會(huì)溢出的內(nèi)存仰美。

執(zhí)行Native方法時(shí)值為空

Native方法大多是通過C實(shí)現(xiàn)并未編譯成需要執(zhí)行的字節(jié)碼指令迷殿,也就不需要去存儲字節(jié)碼文件的行號了。

1.3.2咖杂、虛擬機(jī)棧

方法的出入棧:調(diào)用的方法會(huì)被打包成 棧楨 庆寺,一個(gè)棧楨至少需要包含一個(gè)局部變量表、操作數(shù)棧诉字、楨數(shù)據(jù)區(qū)懦尝、動(dòng)態(tài)鏈接。

動(dòng)態(tài)鏈接:

當(dāng)棧幀內(nèi)部包含一個(gè)指向運(yùn)行時(shí)常量池引用前提下壤圃,類加載時(shí)候會(huì)進(jìn)行符號引用到直接引用的解析跟鏈接替換陵霉。

局部變量表:

局部變量表是棧幀重要組中部分之一。他主要保存函數(shù)的參數(shù)以及局部的變量信息伍绳。局部變量表中的變量作用域是當(dāng)前調(diào)用的函數(shù)撩匕。函數(shù)調(diào)用結(jié)束后,隨著函數(shù)棧幀的銷毀墨叛。局部變量表也會(huì)隨之銷毀止毕,釋放空間。

操作數(shù)棧:

保存著Java虛擬機(jī)執(zhí)行過程中數(shù)據(jù)

方法返回地址:

方法被調(diào)用的位置漠趁,當(dāng)方法退出時(shí)候?qū)嶋H上等同于當(dāng)前棧幀出棧扁凛。

比如執(zhí)行簡單加減法:

執(zhí)行 javap -c *.class :

1.3.3、本地方法棧

跟虛擬機(jī)棧類似闯传,只是為使用到的Native方法服務(wù)而已谨朝。

2、判斷對象是否存活

JVM空間不夠就需要 Garbage Collection 了甥绿,一般共享區(qū)的都要被回收比如堆區(qū)以及方法區(qū)字币。在進(jìn)行內(nèi)存回收之前要做的事情就是 判斷那些對象是死的,哪些是活的 共缕。常用方法有兩種 引用計(jì)數(shù)法 跟 可達(dá)性分析 洗出。

2.1、引用計(jì)數(shù)法

思路是給 Java 對象添加一個(gè)引用計(jì)數(shù)器图谷,每當(dāng)有一個(gè)地方引用它時(shí)翩活,計(jì)數(shù)器 +1;引用失效則 -1便贵,當(dāng)計(jì)數(shù)器不為 0 時(shí)菠镇,判斷該對象存活;否則判斷為死亡(計(jì)數(shù)器 = 0)承璃。

優(yōu)點(diǎn):

實(shí)現(xiàn)簡單利耍,判斷高效。

缺點(diǎn):

無法解決 對象間 相互循環(huán)引用 的問題

step1: GcObject實(shí)例1的引用計(jì)數(shù)+1盔粹,實(shí)例1引用數(shù) = 1

step2: GcObject實(shí)例2的引用計(jì)數(shù)+1隘梨,實(shí)例2引用數(shù) = 1

step3: GcObject實(shí)例2的引用計(jì)數(shù)+1,實(shí)例2引用數(shù) = 2

step4: GcObject實(shí)例1的引用計(jì)數(shù)+1玻佩,實(shí)例1引用數(shù) = 2

step5: GcObject實(shí)例1的引用計(jì)數(shù)-1出嘹,結(jié)果為 1

step6: GcObject實(shí)例2的引用計(jì)數(shù)-1,結(jié)果為 1

如上分析發(fā)現(xiàn)實(shí)例1跟實(shí)例2的引用數(shù)都不為0而又相互引用咬崔,這兩個(gè)實(shí)例所占有的內(nèi)存則無法釋放税稼。

2.2、可達(dá)性分析

很多主流商用語言(如Java垮斯、C#)都采用 引用鏈法 判斷對象是否存活郎仆,大致的思路就是將一系列的 GC Roots 對象作為起點(diǎn),從這些起點(diǎn)開始向下搜索兜蠕。在Java語言中扰肌,可作為 GC Roots的對象包含以下幾種:

第一種是 虛擬機(jī)棧中的引用的對象 ,在程序中正常創(chuàng)建一個(gè)對象熊杨,對象會(huì)在堆上開辟一塊空間曙旭,同時(shí)會(huì)將這塊空間的地址作為引用保存到虛擬機(jī)棧中盗舰,如果對象生命周期結(jié)束了,那么引用就會(huì)從虛擬機(jī)棧中出棧桂躏,因此如果在虛擬機(jī)棧中有引用钻趋,就說明這個(gè)對象還是有用的,這種情況是最常見的剂习。

第二種是我們 在類中定義了全局的靜態(tài)的對象 蛮位,也就是使用了 static 關(guān)鍵字,由于虛擬機(jī)棧是線程私有的鳞绕,所以這種對象的引用會(huì)保存在共有的方法區(qū)中失仁,顯然將方法區(qū)中的靜態(tài)引用作為GC Roots是必須的。

第三種便是 常量引用 们何,就是使用了 static final 關(guān)鍵字萄焦,由于這種引用初始化之后不會(huì)修改,所以方法區(qū)常量池里的引用的對象也應(yīng)該作為GC Roots垂蜗。

第四種是在使用 JNI 技術(shù)時(shí)楷扬,有時(shí)候單純的Java代碼并不能滿足我們的需求,我們可能需要在Java中調(diào)用C或C++的代碼贴见,因此會(huì)使用 Native方法 烘苹,JVM內(nèi)存中專門有一塊本地方法棧,用來保存這些對象的引用片部,所以本地方法棧中引用的對象也會(huì)被作為GC Roots镣衡。

GC Root步驟主要包含如下三步:

2.1.1 可達(dá)性分析

當(dāng)一個(gè)對象到 GC Roots 沒有任何引用鏈相連時(shí) ,則判斷該對象不可達(dá)档悠。

注意: 可達(dá)性分析僅僅只是判斷對象是否可達(dá)廊鸥,但還不足以判斷對象是否存活 / 死亡。

2.1.2 第一次標(biāo)記 & 篩選

篩選的條件對象 如果沒有重寫finalize或者調(diào)用過finalize 則將該對象加入到F-Queue中

2.1.3 第二次標(biāo)記 & 篩選

當(dāng)對象經(jīng)過了第一次的標(biāo)記 & 篩選辖所,會(huì)被進(jìn)行第二次標(biāo)記 & 準(zhǔn)備被進(jìn)行篩選惰说。 經(jīng)過F-Queue篩選后如果對象還沒有跟GC Root建立引用關(guān)系則被回收 ,屬于給個(gè)二次機(jī)會(huì)缘回。

2.3吆视、四大引用類型

2.3.1 強(qiáng)引用

強(qiáng)引用(StrongReference)是使用最普遍的引用。垃圾回收器絕對不會(huì)回收它酥宴,內(nèi)存不足時(shí)寧愿拋出OOM導(dǎo)致程序異常啦吧,平常的new 對象就是。

2.3.2 軟引用

垃圾回收器在內(nèi)存充足時(shí)不會(huì)回收軟引用(SoftReference)對象拙寡,不足時(shí)會(huì)回收它授滓,特別適合用于創(chuàng)建緩存。

2.3.3 弱引用

弱引用(WeakReference)是在掃描到該對象時(shí)無論內(nèi)存是否充足都會(huì)回收該對象。 ThreadLocal 的Key就是弱引用般堆。

2.3.4 虛引用

如果一個(gè)對象只具有虛引用(PhantomReference)那么跟沒有任何引用一樣在孝,任何適合都可以被回收。主要用跟蹤對象跟垃圾回收器回收的活動(dòng)淮摔。

3浑玛、垃圾回收算法

為了揮手回收垃圾操作系統(tǒng)一般會(huì)使用 標(biāo)記清除 、 復(fù)制算法 噩咪、 標(biāo)記整理 三種算法,這三種各有優(yōu)劣极阅,簡單介紹下:

3.1胃碾、標(biāo)記清除

原理:

算法分為 標(biāo)記 和 清除 兩個(gè)階段:首先標(biāo)記出所有需要回收的對象,在標(biāo)記完成后統(tǒng)一回收所有被標(biāo)記的對象筋搏。

缺點(diǎn):

標(biāo)記清除之后會(huì)產(chǎn)生大量不連續(xù)的內(nèi)存碎片仆百,導(dǎo)致觸發(fā)GC。

3.2奔脐、標(biāo)記復(fù)制

原理:

將可用內(nèi)存按容量劃分為大小相等的兩塊俄周,每次只使用其中的一塊。當(dāng)這一塊的內(nèi)存用完了髓迎,就將還存活著的對象復(fù)制到另外一塊上面峦朗,然后再把已使用過的內(nèi)存空間一次清理掉。

缺點(diǎn):

這種算法的代價(jià)是將內(nèi)存縮小為了原來的一半排龄,還要來回移動(dòng)數(shù)據(jù)波势。

3.3、標(biāo)記整理

原理:

首先標(biāo)記出所有需要回收的對象橄维,在標(biāo)記完成后尺铣,后續(xù)步驟是讓所有存活的對象都向一端移動(dòng),然后直接清理掉端邊界以外的內(nèi)存争舞。

缺點(diǎn):

涉及到移動(dòng)大量對象凛忿,效率不高。

總結(jié):

3.4 竞川、三色標(biāo)記跟讀寫屏障

前面說的三種回收算法都說到了先 標(biāo)記 店溢,問題是如何標(biāo)記的呢? **說話說一半流译,小心沒老伴 **逞怨!

接下來的知識點(diǎn)個(gè)人感覺面試應(yīng)該問不到那么深了,但是為了 必須Mark下 福澡! CMS 叠赦、 G1標(biāo)記時(shí)候一般用的是 三色標(biāo)記法 ,根據(jù)可達(dá)性分析從GC Roots開始進(jìn)行遍歷訪問,可達(dá)的則為存活對象除秀,而最終不可達(dá)說明就是需要被GC對象斥扛。大致流程是把遍歷對象圖過程中遇到的對象,按 是否訪問過 這個(gè)條件標(biāo)記成以下三種顏色:

白色:尚未訪問過规揪。

黑色:本對象已訪問過度苔,而且本對象 引用到 的其他對象 也全部訪問過了。

灰色:本對象已訪問過暂吉,但是本對象 引用到 的其他對象 尚未全部訪問完 胖秒。全部訪問后會(huì)轉(zhuǎn)換為黑色。

假設(shè)現(xiàn)在有白慕的、灰阎肝、黑三個(gè)集合(表示當(dāng)前對象的顏色),遍歷訪問過程:

1肮街、初始時(shí)所有對象都在白色集合中风题。

2、將GC Roots 直接引用到的對象挪到灰色集合中嫉父。

3沛硅、從灰色集合中獲取對象:第一步將本對象 引用到的 其他對象 全部挪到灰色集合中,第二步將本對象 挪到黑色集合里面绕辖。

4摇肌、重復(fù)步驟3,直至灰色集合為空時(shí)結(jié)束引镊。

5朦蕴、結(jié)束后仍在白色集合的對象即為GC Roots 不可達(dá) ,可以嘗試進(jìn)行回收弟头。

當(dāng)STW時(shí)對象間的引用是不會(huì)發(fā)生變化的吩抓,可以輕松完成標(biāo)記。當(dāng)支持并發(fā)標(biāo)記時(shí)赴恨,對象間的引用可能發(fā)生變化疹娶,多標(biāo)和漏標(biāo)的情況就有可能發(fā)生。

3.4 .1伦连、浮動(dòng)垃圾

狀況:GC線程遍歷到E(E是灰色)雨饺,一個(gè)業(yè)務(wù)線程執(zhí)行了D.E = null,此時(shí)E應(yīng)該被回收的惑淳。但是GC線程已經(jīng)認(rèn)為E是灰色了會(huì)繼續(xù)遍歷额港,導(dǎo)致E沒有被回收。

3.4 .2歧焦、漏標(biāo)

GC線程遍歷到E(灰色了)移斩。業(yè)務(wù)線程執(zhí)行了E–>G斷開,D–>G鏈接的操作。GC線程發(fā)現(xiàn)E無法到達(dá)G向瓷,因?yàn)槭呛谏粫?huì)再遍歷標(biāo)記了肠套。最終導(dǎo)致漏標(biāo)G。

漏標(biāo)的必備兩個(gè)條件: 灰到白斷開 猖任, 黑到白建立 你稚。

漏標(biāo)解決方法:

將對象G存儲到特定集合中,等并發(fā)標(biāo)記遍歷完畢后再對集合中對象進(jìn)行 重新標(biāo)記 朱躺。

3.4.2.1刁赖、CMS方案

這里比如開始B指向C,但是后來B不指向C长搀,A指向D乾闰,最簡單的方法是 將A變成灰色 ,等待下次進(jìn)行再次遍歷盈滴。

CMS中可能引發(fā) ABA 問題:

1、回收線程 m1 正在標(biāo)記A轿钠,屬性A.1標(biāo)記完畢巢钓,正在標(biāo)記屬性A.2。

2疗垛、業(yè)務(wù)線程 m2 把屬性1指向了C症汹,由于CMS方案此時(shí)回收線程 m3 把A標(biāo)記位灰色。

3贷腕、回收線程 m1 認(rèn)為所有屬性標(biāo)記完畢背镇,將A設(shè)置為黑色,結(jié)果C漏標(biāo)泽裳。所以CMS階段需要重新標(biāo)記瞒斩。

3.4.2.2、讀寫屏障

漏標(biāo)的實(shí)現(xiàn)是有三步的涮总,JVM加入了讀寫屏障胸囱,其中讀屏障則是攔截第一步,寫屏障用于攔截第二和第三步瀑梗。

寫屏障 + SATB(原始快照) 來破壞 灰到白斷開烹笔。

寫屏障 + 增量更新 來破壞 黑到白建立。

讀屏障 一種保守方式來破壞灰到白斷開后白的存儲抛丽,此時(shí)用讀屏障OK的谤职。

現(xiàn)代使用可達(dá)性分析的垃圾回收器幾乎都借鑒了三色標(biāo)記的算法思想,盡管實(shí)現(xiàn)的方式不盡相同亿鲜。對于讀寫屏障允蜈,以Java HotSpot VM為例,其 并發(fā)標(biāo)記時(shí)對漏標(biāo) 的處理方案如下:

CMS : 寫屏障 + 增量更新

G1 : 寫屏障 + SATB

ZGC : 讀屏障

CMS中使用的增量更新,在重新標(biāo)記階段除了需要遍歷 寫屏障的記錄陷寝,還 需要重新掃描遍歷GC Roots(標(biāo)記過的不用再標(biāo)記)锅很,這是由于CMS對于astore_x等指令不添加寫屏障的原因。

4凤跑、GC流程

核心思想就是 根據(jù)各個(gè)年代的特點(diǎn)不同選用不同到垃圾收集算法 爆安。

年輕代 :使用 復(fù)制算法

老年代 : 使用 標(biāo)記整理 或者 標(biāo)記清除 算法。

為什么要有年輕代:

分代的好處就是 優(yōu)化GC性能 仔引,如果沒有分代每次掃描所有區(qū)域能累死GC扔仓。因?yàn)楹芏鄬ο髱缀蹙褪?朝生夕死 的,如果分代的話咖耘,我們把新創(chuàng)建的對象放到某一地方翘簇,當(dāng)GC的時(shí)候先把這塊存 朝生夕死 (80%以上)對象的區(qū)域進(jìn)行回收,這樣就會(huì)騰出很大的空間出來儿倒。

4.1版保、 年輕代

HotSpot JVM把年輕代分為了三部分:1個(gè) Eden 區(qū)和2個(gè) Survivor 區(qū)(分別叫 fromto )。默認(rèn)比例為 8:1:1 夫否。一般情況下彻犁,新創(chuàng)建的對象都會(huì)被分配到 Eden 區(qū)(一些大對象特殊處理),這些對象經(jīng)過第一次Minor GC后凰慈,如果仍然存活汞幢,將會(huì)被移到 Survivor 區(qū)。對象在Survivor區(qū)中每熬過一次 Minor GC 年齡就會(huì)增加1歲微谓,當(dāng)它的年齡增加到一定次數(shù)(默認(rèn) 15 次)時(shí)森篷,就會(huì)被移動(dòng)到年老代中。年輕代的垃圾回收算法使用的是 復(fù)制算法 豺型。

年輕代GC過程:

GC開始前仲智,年輕代對象只會(huì)存在于 Eden 區(qū)和名為 FromSurvivor 區(qū),名為 ToSurvivor 區(qū)永遠(yuǎn)是空的姻氨。如果新分配對象在 Eden 申請空間發(fā)現(xiàn)不足就會(huì)導(dǎo)致GC坎藐。

yang GC : Eden 區(qū)中所有存活的對象都會(huì)被復(fù)制到 To ,而在 From 區(qū)中哼绑,仍存活的對象會(huì)根據(jù)他們的年齡值來決定去向岩馍。年齡達(dá)到一定值(年齡閾值可以通過 -XX:MaxTenuringThreshold來設(shè)置)的對象會(huì)被移動(dòng)到年老代中,沒有達(dá)到閾值的對象會(huì)被復(fù)制到 To 區(qū)域抖韩。經(jīng)過這次GC后蛀恩, Eden 區(qū)和 From 區(qū)已經(jīng)被清空。這個(gè)時(shí)候茂浮, FromTo 會(huì)交換他們的角色双谆,也就是新的 To 就是上次GC前的 From 壳咕,新的 From 就是上次GC前的 To 。不管怎樣 都會(huì)保證名為To的Survivor區(qū)域是空的 顽馋。 Minor GC 會(huì)一直重復(fù)這樣的過程谓厘,直到 To 區(qū)被填滿, To 區(qū)被填滿之后寸谜,會(huì)將所有對象移動(dòng)到年老代中竟稳。這里注意如果yang GC 后空間還是不夠用則會(huì) **空間擔(dān)保 **機(jī)制將數(shù)據(jù)送到Old區(qū)

卡表 Card Table:

為了支持高頻率的新生代回收 ,虛擬機(jī)使用一種叫做 卡表 (Card Table)的數(shù)據(jù)結(jié)構(gòu)熊痴,卡表作為一個(gè)比特位的集合他爸, 每一個(gè)比特位可以用來表示年老代的某一區(qū)域中的所有對象是否持有新生代對象的引用 。

新生代GC時(shí)不用花大量的時(shí)間掃描所有年老代對象果善,來確定每一個(gè)對象的引用關(guān)系诊笤, 先掃描卡表 ,只有卡表的標(biāo)記位為1時(shí)巾陕,才需要掃描給定區(qū)域的年老代對象讨跟。而卡表位為0的所在區(qū)域的年老代對象,一定不包含有對新生代的引用鄙煤。

4.2许赃、 老年代

老年代GC過程:

老年代中存放的對象是存活了很久的,年齡大于15的對象 或者 觸發(fā)了老年代的 分配擔(dān)保 機(jī)制存儲的大對象馆类。在老年代觸發(fā)的gc叫 major gc 也叫 full gc 。 full gc會(huì)包含年輕代的gc 弹谁。 full gc 采用的是 標(biāo)記-清除標(biāo)記整理 乾巧。在執(zhí)行 full gc 的情況下,會(huì)阻塞程序的正常運(yùn)行预愤。老年代的gc比年輕代的gc效率上 慢10倍以上 沟于。對效率有很大的影響。所以 一定要盡量避免老年代GC 植康!

4.3旷太、 元空間

永久代的回收會(huì)隨著 full gc 進(jìn)行移動(dòng),消耗性能销睁。每種類型的垃圾回收都需要特殊處理 元數(shù)據(jù)供璧。將元數(shù)據(jù)剝離出來,簡化了垃圾收集冻记,提高了效率睡毒。

-XX:MetaspaceSize 初始空間的大小。達(dá)到該值就會(huì)觸發(fā)垃圾收集進(jìn)行類型卸載冗栗,同時(shí)GC會(huì)對該值進(jìn)行調(diào)整:

如果釋放了大量的空間演顾,就適當(dāng)降低該值供搀;

如果釋放了很少的空間,那么在不超過MaxMetaspaceSize時(shí)钠至,適當(dāng)提高該值葛虐。

-XX:MaxMetaspaceSize:

最大空間,默認(rèn)是沒有限制的棉钧。

4.4 屿脐、垃圾回收流程總結(jié)

大致的 GC回收流程 如 上圖 ,還有一種設(shè)置就是 大對象直接進(jìn)入老年代

如果在新生代分配失敗且對象是一個(gè)不含任何對象引用的大數(shù)組掰盘,可被直接分配到老年代摄悯。通過在老年代的分配避免新生代的一次垃圾回收。

設(shè)置了-XX:PretenureSizeThreshold 值愧捕,任何比這個(gè)值大的對象都不會(huì)嘗試在新生代分配奢驯,將在老年代分配內(nèi)存。

內(nèi)存回收跟分配策略

優(yōu)先在Eden上分配對象次绘,此區(qū)域垃圾回收頻繁速度還快瘪阁。

大對象直接進(jìn)入老生代。

年長者(長期存活對象默認(rèn)15次)跟 進(jìn)入老生代邮偎。

在Survivor空間中相同年齡所有對象大小的總和大于Survivor空間的一半管跺,年齡大于或等于該年齡的對象會(huì)群體進(jìn)入老生代。

空間分配擔(dān)保(擔(dān)保minorGC)禾进,如果Minor GC后 Survivor區(qū)放不下新生代仍存活的對象豁跑,把Suvivor 無法容納的對象直接進(jìn)入老年代。

5泻云、垃圾收集器

5.1艇拍、 垃圾收集器

堆heap是垃圾回收機(jī)制的重點(diǎn)區(qū)域。我們知道垃圾回收機(jī)制有三種 minor gc 宠纯、 major gc 和 full g c卸夕。針對于堆的就是前兩種。年輕代的叫 minor gc 婆瓜,老年代的叫 major gc 快集。

JDK7、JDK8 默認(rèn)垃圾收集器 Parallel Scavenge(新生代)+ Parallel Old(老年代)

JDK9 默認(rèn)垃圾收集器 G1 廉白,服務(wù)端開發(fā)常見組合就是 ParNew + CMS

工程化使用的時(shí)候使用指定的垃圾收集器組合使用个初,講解垃圾收集器前先普及幾個(gè)重要知識點(diǎn):

STW

java中 Stop-The-World 機(jī)制簡稱STW,是指執(zhí)行垃圾收集算法時(shí)Java應(yīng)用程序的 其他所有線程都被掛起 (除了垃圾收集幫助器之外)猴蹂。是Java中一種全局暫筒颍現(xiàn)象,全局停頓晕讲,所有Java代碼停止覆获,native代碼雖然可以執(zhí)行但不能與JVM交互马澈,如果發(fā)生了STW 現(xiàn)象多半是由于gc引起

吞吐量

吞吐量 = 運(yùn)行用戶代碼時(shí)間 / ( 運(yùn)行用戶代碼時(shí)間 + 垃圾收集時(shí)間 )弄息。例如:虛擬機(jī)共運(yùn)行100分鐘痊班,垃圾收集器花掉1分鐘,那么吞吐量就是99%

垃圾收集時(shí)間

垃圾回收頻率 * 單次垃圾回收時(shí)間

并行收集

指多條垃圾收集線程并行工作摹量,但此時(shí)用戶線程仍 處于等待狀態(tài) 涤伐。

并發(fā)收集

用戶線程與垃圾收集線程同時(shí)工作 (不一定是并行的可能會(huì)交替執(zhí)行)。用戶程序在繼續(xù)運(yùn)行缨称,而垃圾收集程序運(yùn)行在另一個(gè)CPU上凝果。

5.2、 新生代

新生代有 Serial 睦尽、 ParNew 器净、 Parallel Scavenge 三種垃圾收集器。

5.3当凡、 老年代

老年代有 Serial Old 山害、 Parallel Old 、 CMS 三種垃圾收集器沿量。

5.3.1浪慌、CMS

CMS (Concurrent Mark Sweep)比較重要這里 重點(diǎn)說一下

CMS的初衷和目的:

為了消除Throught收集器和Serial收集器在Full GC周期中的長時(shí)間停頓朴则。是一種 以獲取最短回收停頓時(shí)間為目標(biāo) 的收集器权纤,具有自適應(yīng)調(diào)整策略,適合互聯(lián)網(wǎng)站 跟B/S 服務(wù)應(yīng)用乌妒。

CMS的適用場景:

如果你的應(yīng)用需要 更快的響應(yīng) 汹想,不希望有長時(shí)間的停頓,同時(shí)你的 CPU資源也比較豐富 芥被,就適合使用CMS收集器。比如常見的Server端任務(wù)坐榆。

優(yōu)點(diǎn):

并發(fā)收集拴魄、低停頓。

缺點(diǎn):

CMS收集器對CPU資源非常敏感 :在并發(fā)階段席镀,雖然不會(huì)導(dǎo)致用戶線程停頓匹中,但是會(huì)占用CPU資源而導(dǎo)致引用程序變慢,總吞吐量下降豪诲。

無法處理浮動(dòng)垃圾 :由于CMS并發(fā)清理階段用戶線程還在運(yùn)行顶捷,伴隨程序的運(yùn)行自然會(huì)有新的垃圾不斷產(chǎn)生,這一部分垃圾出現(xiàn)在標(biāo)記過程之后屎篱,CMS無法在本次收集中處理它們服赎,只好留待下一次GC時(shí)將其清理掉葵蒂。這一部分垃圾稱為 浮動(dòng)垃圾 。 如果內(nèi)存放不下浮動(dòng)垃圾這時(shí) JVM 啟動(dòng) Serial Old 替代 CMS 重虑。

空間碎片 :CMS是基于 標(biāo)記-清除 算法實(shí)現(xiàn)的收集器践付,使用 標(biāo)記-清除 算法收集后,會(huì)產(chǎn)生 大量碎片 缺厉。

CMS回收流程:

初始標(biāo)記 : 引發(fā)STW 永高, 僅僅只是標(biāo)記出GC ROOTS能直接關(guān)聯(lián)到的對象,速度很快提针。

并發(fā)標(biāo)記 : 不引發(fā)STW 命爬,正常運(yùn)行 所有Old 對象是否可鏈到GC Roots

重新標(biāo)記 : 引發(fā)STW ,為了修正并發(fā)標(biāo)記期間辐脖,因用戶程序繼續(xù)運(yùn)作而導(dǎo)致標(biāo)記產(chǎn)生改變的標(biāo)記饲宛。這個(gè)階段的停頓時(shí)間會(huì)被初始標(biāo)記階段稍長,但比并發(fā)標(biāo)記階段要短揖曾。

并發(fā)清除 : 不引發(fā)STW 落萎,正常運(yùn)行,標(biāo)記清除算法來清理刪除掉標(biāo)記階段判斷的已經(jīng)死亡的對象炭剪。

總結(jié):

并發(fā)標(biāo)記 和 并發(fā)清除 的耗時(shí)最長但是不需要停止用戶線程练链。 初始標(biāo)記 和 重新標(biāo)記 的耗時(shí)較短,但是需要停止用戶線程奴拦,所以整個(gè)GC過程造成的停頓時(shí)間較短媒鼓,大部分時(shí)候是可以和用戶線程一起工作的。

之前的GC收集器對Heap的劃分:

以前垃圾回收器是 新生代 + 老年代 错妖,用了CMS效果也不是很好绿鸣,為了減少STW對系統(tǒng)的影響引入了G1(Garbage-First Garbage Collector), G1 是一款面向服務(wù)端應(yīng)用的垃圾收集器暂氯,具有如下特點(diǎn):

1潮模、 并行與并發(fā) :G1能充分利用多CPU、多核環(huán)境下的硬件優(yōu)勢痴施,可以通過并發(fā)的方式讓Java程序繼續(xù)執(zhí)行擎厢。

2、 分代收集 :分代概念在G1中依然得以保留辣吃,它能夠采用不同的方式去處理新創(chuàng)建的對象和已經(jīng)存活了一段時(shí)間动遭、熬過多次GC的舊對象來獲得更好的收集效果。

3神得、 空間整合 :G1從整體上看是基于 標(biāo)記-整理 算法實(shí)現(xiàn)的厘惦,從局部(兩個(gè)Region之間)上看是基于 復(fù)制算法 實(shí)現(xiàn)的,G1運(yùn)行期間不會(huì)產(chǎn)生內(nèi)存空間碎片哩簿。

4宵蕉、 可預(yù)測停頓 :G1比CMS牛在能建立可預(yù)測的停頓時(shí)間模型酝静,能讓使用者明確指定在一個(gè)長度為M毫秒的時(shí)間片段內(nèi),消耗在垃圾收集上的時(shí)間不得超過N毫秒国裳。

G1作為JDK9之后的服務(wù)端默認(rèn)收集器形入,不再區(qū)分年輕代和老年代進(jìn)行垃圾回收,G1默認(rèn)把堆內(nèi)存分為N個(gè)分區(qū)缝左,每個(gè)1~32M(總是2的冪次方)亿遂。并且提供了四種不同Region標(biāo)簽 Eden 、 Survivor 渺杉、 Old 蛇数、 Humongous 。H區(qū)可以認(rèn)為是Old區(qū)中一種特別專門用來存儲大數(shù)據(jù)的是越,關(guān)于H區(qū)數(shù)據(jù)存儲類型一般符合下面條件:

當(dāng) 0.5 Region <= 當(dāng)對象大小 <= 1 Region 時(shí)候?qū)?shù)據(jù)存儲到 H區(qū)

當(dāng)對象大小 > 1 Region 存儲到連續(xù)的H區(qū)耳舅。

同時(shí)G1中引入了 RememberSets 、 CollectionSets 幫助更好的執(zhí)行GC 倚评。

1浦徊、 RememberSets : RSet 記錄了其他Region中的對象引用本Region中對象的關(guān)系,屬于points-into結(jié)構(gòu)(誰引用了我的對象)

2天梧、 CollectionSets : Csets 是一次GC中需要被清理的regions集合盔性,注意G1每次GC不是全部region都參與的,可能只清理少數(shù)幾個(gè)呢岗,這幾個(gè)就被叫做Csets冕香。在GC的時(shí)候,對于old -> young 和old -> old的跨代對象引用后豫,只要掃描對應(yīng)的 CSet 中的 RSet 即可悉尾。

G1進(jìn)行GC的時(shí)候一般分為 Yang GC 跟 Mixed GC 。

Young GC : CSet 就是所有年輕代里面的Region

Mixed GC : CSet 是所有年輕代里的Region加上在全局并發(fā)標(biāo)記階段標(biāo)記出來的收益高的Region

5.4.1挫酿、Yang GC

標(biāo)準(zhǔn)的年輕代GC算法构眯,整體思路跟CMS中類似。

5.4.2早龟、Mixed GC

G1中是 有Old GC的惫霸,有一個(gè)把老年代跟新生代同時(shí)GC的 Mixed GC,它的 回收流程

1拄衰、 初始標(biāo)記 : 是STW事件 它褪,其完成工作是標(biāo)記GC ROOTS 直接可達(dá)的對象饵骨。標(biāo)記位RootRegion翘悉。

2、 根區(qū)域掃描 : 不是STW事件 居触,拿來RootRegion妖混,掃描整個(gè)Old區(qū)所有Region老赤,看每個(gè)Region的 Rset 中是否有RootRegion。有則標(biāo)識出來制市。

3抬旺、 并發(fā)標(biāo)記 : 同CMS并發(fā)標(biāo)記 不需要STW ,遍歷范圍減少祥楣,在此只需要遍歷 第二步 被標(biāo)記到引用老年代的對象 RSet开财。

4、 最終標(biāo)記 : 同 CMS 重新標(biāo)記 會(huì)STW 误褪,用的 SATB 操作责鳍,速度更快。

5兽间、 清除 : STW操作 历葛,用 復(fù)制清理算法 ,清點(diǎn)出有存活對象的Region和沒有存活對象的Region(Empty Region)嘀略,更新Rset恤溶。把Empty Region收集起來到可分配Region隊(duì)列。

回收總結(jié):

1帜羊、經(jīng)過global concurrent marking咒程,collector就知道哪些Region有存活的對象。并將那些完全可回收的Region(沒有存活對象)收集起來加入到可分配Region隊(duì)列逮壁,實(shí)現(xiàn)對該部分內(nèi)存的回收孵坚。對于有存活對象的Region,G1會(huì)根據(jù)統(tǒng)計(jì)模型找出收益最高窥淆、開銷不超過用戶指定的上限的若干Region進(jìn)行對象回收卖宠。這些選中被回收的Region組成的集合就叫做collection set 簡稱Cset!

2忧饭、在MIX GC中的Cset = 所有年輕代里的region + 根據(jù)global concurrent marking統(tǒng)計(jì)得出收集收益高的若干old region 扛伍。

3、在YGC中的Cset = 所有年輕代里的region + 通過控制年輕代的region個(gè)數(shù)來控制young GC的開銷 词裤。

4刺洒、YGC 與 MIXGC 都是采用多線程復(fù)制清理,整個(gè)過程會(huì)STW吼砂。 G1的 低延遲原理 在于其回收的區(qū)域變得精確并且范圍變小了逆航。

G1提速點(diǎn):

1 重新標(biāo)記 使X區(qū)域直接刪除。

2 Rset 降低了掃描的范圍渔肩,上題中兩點(diǎn)巢株。

3 重新標(biāo)記階段使用 SATB 速度比CMS快脱盲。

4 清理過程為選取部分存活率低的Region進(jìn)行清理恃鞋,不是全部贴唇,提高了清理的效率。

總結(jié):

就像你把房子打掃干凈,你可能只把顯眼而比較大的垃圾打掃了,犄角旮旯的你沒打掃。
一句話總結(jié)G1思維: 每次選擇性的清理大部分垃圾來保證時(shí)效性跟系統(tǒng)的正常運(yùn)行 胡嘿。

鏈接:http://www.reibang.com/p/7340becc027c

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市钳踊,隨后出現(xiàn)的幾起案子衷敌,更是在濱河造成了極大的恐慌,老刑警劉巖拓瞪,帶你破解...
    沈念sama閱讀 217,542評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件逢享,死亡現(xiàn)場離奇詭異,居然都是意外死亡吴藻,警方通過查閱死者的電腦和手機(jī)瞒爬,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,822評論 3 394
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來沟堡,“玉大人侧但,你說我怎么就攤上這事『铰蓿” “怎么了禀横?”我有些...
    開封第一講書人閱讀 163,912評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長粥血。 經(jīng)常有香客問我柏锄,道長,這世上最難降的妖魔是什么复亏? 我笑而不...
    開封第一講書人閱讀 58,449評論 1 293
  • 正文 為了忘掉前任趾娃,我火速辦了婚禮,結(jié)果婚禮上缔御,老公的妹妹穿的比我還像新娘抬闷。我一直安慰自己,他們只是感情好耕突,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,500評論 6 392
  • 文/花漫 我一把揭開白布笤成。 她就那樣靜靜地躺著,像睡著了一般眷茁。 火紅的嫁衣襯著肌膚如雪炕泳。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,370評論 1 302
  • 那天上祈,我揣著相機(jī)與錄音培遵,去河邊找鬼挣磨。 笑死,一個(gè)胖子當(dāng)著我的面吹牛荤懂,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播塘砸,決...
    沈念sama閱讀 40,193評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼节仿,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了掉蔬?” 一聲冷哼從身側(cè)響起廊宪,我...
    開封第一講書人閱讀 39,074評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎女轿,沒想到半個(gè)月后箭启,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,505評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡蛉迹,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,722評論 3 335
  • 正文 我和宋清朗相戀三年傅寡,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片北救。...
    茶點(diǎn)故事閱讀 39,841評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡荐操,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出珍策,到底是詐尸還是另有隱情托启,我是刑警寧澤,帶...
    沈念sama閱讀 35,569評論 5 345
  • 正文 年R本政府宣布攘宙,位于F島的核電站屯耸,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏蹭劈。R本人自食惡果不足惜疗绣,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,168評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望铺韧。 院中可真熱鬧持痰,春花似錦、人聲如沸祟蚀。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,783評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽前酿。三九已至患雏,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間罢维,已是汗流浹背淹仑。 一陣腳步聲響...
    開封第一講書人閱讀 32,918評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人匀借。 一個(gè)月前我還...
    沈念sama閱讀 47,962評論 2 370
  • 正文 我出身青樓颜阐,卻偏偏與公主長得像,于是被迫代替她去往敵國和親吓肋。 傳聞我的和親對象是個(gè)殘疾皇子凳怨,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,781評論 2 354

推薦閱讀更多精彩內(nèi)容

  • Java開發(fā)相對于C語言最方便的點(diǎn),就是代碼上不需要主動(dòng)去管理內(nèi)存的回收是鬼,而由JVM負(fù)責(zé)分配回收肤舞。 GC算法 標(biāo)記...
    DoubleFooker閱讀 292評論 0 4
  • 今天感恩節(jié)哎,感謝一直在我身邊的親朋好友均蜜。感恩相遇李剖!感恩不離不棄。 中午開了第一次的黨會(huì)囤耳,身份的轉(zhuǎn)變要...
    迷月閃星情閱讀 10,564評論 0 11
  • 彩排完篙顺,天已黑
    劉凱書法閱讀 4,217評論 1 3
  • 沒事就多看看書,因?yàn)楦褂性姇鴼庾匀A充择,讀書萬卷始通神慰安。沒事就多出去旅游,別因?yàn)闆]錢而找借口聪铺,因?yàn)橹灰闶〕詢€用化焕,來...
    向陽之心閱讀 4,784評論 3 11
  • 表情是什么,我認(rèn)為表情就是表現(xiàn)出來的情緒铃剔。表情可以傳達(dá)很多信息撒桨。高興了當(dāng)然就笑了,難過就哭了键兜。兩者是相互影響密不可...
    Persistenc_6aea閱讀 125,019評論 2 7