麻豆小视频在线观看_中文黄色一级片_久久久成人精品_成片免费观看视频大全_午夜精品久久久久久久99热浪潮_成人一区二区三区四区

首頁 > 學(xué)院 > 開發(fā)設(shè)計 > 正文

JVM-GC設(shè)計思路分析

2019-11-10 20:14:48
字體:
供稿:網(wǎng)友

原文地址http://blog.csdn.net/zhshulin/article/details/50583724

java中將內(nèi)存的控制交給JVM來實(shí)現(xiàn),方便了JAVA程序猿,當(dāng)然犧牲了一部分效率,不過總體來看是值得的。那么JVM中是如何設(shè)計GC的呢,本文從幾個問題入手,然后分析了一下設(shè)計思路,如果有理解錯誤的地方,請批評指正!主要參考了《深入理解JAVA虛擬機(jī)》這本書,圖是盜來的,圖的內(nèi)容和書上一樣。

    在JVM的內(nèi)存模型中,堆內(nèi)存是JAVA內(nèi)存區(qū)域中最大的一部分,GC主要就是發(fā)生在堆中,用來回收那些無用的對象。這樣直接就引申出了第一個問題:什么樣的對象需要被回收?判斷條件是什么?如何判斷?

    先談?wù)勈裁磳ο笮枰换厥眨琌K,我們自己想一想,肯定是沒用的對象需要被回收,對吧?那么如何判斷哪些對象還有用,哪些沒用了呢?一個對象被創(chuàng)建,如果被引用了,那這個對象肯定是有用的對吧,如果引用全失效了,那就是沒用的對象了,需要被回收。基于這個思想,引用計數(shù)法誕生了。引用計數(shù)算法:這個非常容易理解,給每個對象添加一個引用計數(shù)器,對象每被引用一次,引用計數(shù)器就+1,引用失效時就-1。那么判斷一個對象是否有用的條件就變成了對這個計數(shù)器值得判斷了,如果為0,那么被回收,如果為>0,那么保留。但是這種方式會產(chǎn)生一個問題,就是對象之間的循環(huán)引用無法被識別,即使這兩個對象不能被訪問,但是它們之間互相引用著對方,故而計數(shù)器肯定>0,那么就不能被回收。JVM中并沒有使用引用計數(shù)算法,而是使用了根搜索算法。根搜索算法:這個算法也不難理解,通過條件,選擇一系列的對象成為“GC Roots"對象,然后將”GC Roots"對象作為起始點(diǎn)開始向下搜索,搜索所有走過的路徑成為“引用鏈”。在這個引用鏈上的對象就保留,而如果一個或多個互相引用的對象不在這個引用鏈上,或者說對象到“GC Roots"不可達(dá),那么這些就是無用的對象,都需要被回收。

注:Java語言中,可作為GC Roots的對象包括下面幾種:

1) 虛擬機(jī)棧(棧幀中的本地變量表)中引用的對象

2) 方法區(qū)中類靜態(tài)屬性引用的對象

3) 方法區(qū)中常量引用的對象

4) 本地方法棧中JNI(即一般說的Native方法)引用的對象

既然根搜索算法需要考慮到對象之間的引用,那么就要說一下JAVA中對象的引用類型了:

從JDK1.2之后,Java對引用的概念進(jìn)行了擴(kuò)充,將引用分為強(qiáng)引用,軟引用,弱引用,虛引用,這四種引用的強(qiáng)度依次減弱

1) 強(qiáng)引用就是指在程序代碼之中普遍存在的,類似 “Object obj = new Object()” 這類的引用,只要強(qiáng)引用還存在,垃圾回收器永遠(yuǎn)不會回收被引用的對象。我們也正是利用這個原理來重現(xiàn)了OOM異常。

2) 軟引用(SoftReference類)是用來描述一些還有用但并非需要的對象,對于軟引用關(guān)聯(lián)著的對象,在系統(tǒng)將要發(fā)生內(nèi)存異常之前,將會把這些對象列進(jìn)回收范圍之中進(jìn)行第二次回收,如果這次回收還沒有足夠的內(nèi)存,才會拋出內(nèi)存異常

3) 弱引用(WeakReference類)也是用來描述非必需對象的,被弱引用關(guān)聯(lián)的對象只能生存到下一次GC發(fā)生之前,當(dāng)垃圾收集器工作時,無論當(dāng)前內(nèi)存釋放足夠,都會回收掉只被弱引用關(guān)聯(lián)的對象

4) 虛引用(PhantomReference類)也稱為幽靈引用或者幻影引用,它是最弱的一種引用關(guān)系,一個對象是否有虛引用的存在,完全不會對其生存時間構(gòu)成影響,也無法通過虛引用來取得一個對象實(shí)例,對一個對象設(shè)置虛引用關(guān)聯(lián)的唯一目的就是能在這個對象被收集器回收時收到一個系統(tǒng)通知

    那么上述內(nèi)容看完之后想必都知道了什么樣的對象會被GC了吧,那么JVM又是通過什么方式來回收這些內(nèi)存的呢?下面就需要了解一下垃圾的回收算法了。

標(biāo)記-清除算法    試著想一想,如果要你要設(shè)計一個算法清除滿足收集條件的對象來釋放內(nèi)存的時候你該怎么做呢?最簡單的是不是就是把需要回收的對象標(biāo)記一下,然后直接全部回收就行了?照著這個思路就是”標(biāo)記-清除算法”的思想了,算法分為“標(biāo)記”和“清除”兩個階段:首先標(biāo)記出所有需要回收的對象,在標(biāo)記完成后統(tǒng)一回收掉所有被標(biāo)記的對象。想法很簡單,實(shí)際也就是這么做的。但是呢,這種方式是不是最好的?有什么缺陷?    想到這里,就需要分析一下了。一個個的標(biāo)記然后清除,效率高嗎?當(dāng)然不。看看下圖的標(biāo)記-清除算法的示意圖,可以發(fā)現(xiàn),標(biāo)記-清除之后會產(chǎn)生大量的內(nèi)存碎片,如果碎片太多,當(dāng)程序運(yùn)行沒有足夠連續(xù)的內(nèi)存空間來存放大對象的時候,就會不得不提前觸發(fā)一次GC。概括來說就是有兩個缺點(diǎn):效率不高;內(nèi)存碎片可能導(dǎo)致提前發(fā)生GC。    學(xué)習(xí)算法的童鞋應(yīng)該都很清楚,效率是很重要的,有時候需要使用空間來換時間提高效率,那么就需要了解一下第二種回收算法了——復(fù)制算法。

復(fù)制算法    復(fù)制算法呢?它的思想就是空間換時間,將內(nèi)存容量劃分成相等的兩塊,當(dāng)這一塊的內(nèi)存用完了,就將還存活的內(nèi)存復(fù)制到另一塊上,然后再把使用過的內(nèi)存空間一次性清理干凈。這樣每次都是對其中的一塊的內(nèi)存進(jìn)行回收,也就不需要考慮內(nèi)存碎片等復(fù)雜情況了,只需要移動堆頂指針,然后按照順序分配即可,實(shí)現(xiàn)簡單,運(yùn)行高效。但是缺點(diǎn)也很明顯:內(nèi)存變成一半了.......下圖就是復(fù)制算法的示意圖:

    我們知道,在JVM中堆內(nèi)存的新生代(new )中的對象存活率較低,采用復(fù)制算法每次需要復(fù)制的對象也不是很多,效率較高,空間換時間值得的。現(xiàn)在的商業(yè)虛擬機(jī)都是采用復(fù)制算法來回收新生代,IBM的專門研究表明:新生代中對象98%是朝生夕死,所以并不需要按照1:1的比例來劃分空間來實(shí)現(xiàn)復(fù)制算法,而是將內(nèi)存分為一塊較大的Eden空間和兩塊較小的Survivor空間,每次使用Eden空間和其中一個Survivor空間。當(dāng)發(fā)生GC的時候,將Eden空間和Survivor空間中還存活的對象拷貝到另一個沒使用的Survivor空間中,然后再清理掉Eden和剛剛使用的Survivor空間。Hotspot虛擬機(jī)默認(rèn)Eden和Survivor的大小比例是8:1,也就是新生代每次可以使用的內(nèi)存空間是整個新生代的90%,只有10%的空間會被浪費(fèi)。    OK,通過上述的分析,我們知道了在JVM中對于新生帶的垃圾回收使用的復(fù)制算法(此時發(fā)生的GC成為young gc),效率高,我們也就只犧牲了10%的內(nèi)存空間,挺不錯的。請注意這里提到的young gc,后面會提到full gc。但是雖然IBM研究表明一般情況下有98%的對象是朝生夕死,需要回收的,但是不能保證每次回收的時候?qū)ο蟮拇婊盥识嫉陀?0%啊,是不是?一旦超過了10%,那么空閑的survivor空間就不夠用了,此時就必須依賴?yán)夏甏目臻g來進(jìn)行分配擔(dān)保(就相當(dāng)于A找B借錢,C替A做擔(dān)保,保證如果A換不起就自己來還,C就是擔(dān)保人,映射到內(nèi)存中老年代所占內(nèi)存就是擔(dān)保人)。如果空閑的Survivor空間無法存放上次GC之后的存活對象,那么這些對象就會通過分配擔(dān)保機(jī)制進(jìn)入老年代。    老年代呢,里面保存的都是生存周期較長的對象(老年代里面的對象都是經(jīng)過了新生代,然后多次存活下來的對象),而復(fù)制算法在應(yīng)對這種存活率極高的內(nèi)存區(qū)域的對象回收時,需要執(zhí)行較多的復(fù)制操作,效率將會變低。關(guān)鍵的還是如果不想浪費(fèi)50%的空間,那么就需要分配擔(dān)保機(jī)制(參考新生代的設(shè)計),但是并沒有額外的空間來擔(dān)保了。所以對于老年代的特性,有人提出了一種“標(biāo)記-整理算法”,看到這里肯定就想到了前面提到的“標(biāo)記-清除算法“了,OK,這兩個算法標(biāo)記的過程都是一樣的,就在于”標(biāo)記-整理算法”不是直接對可回收對象進(jìn)行清理,而是讓所有存活的對象都向一端移動,然后直接清理掉端邊界以外的內(nèi)存,示意圖如下圖所示。    很明顯,這種”標(biāo)記-整理算法“的效率不高,所以如果老年代發(fā)生GC,那么效率也就不高了,并且一旦老年代發(fā)生GC,那么發(fā)生的必然是Full GC ,Full GC 會同時對老年代和新生代進(jìn)行GC操作,順便也會回收一下perm gen中的內(nèi)存,所以相比較young gc來說很慢,我們在JVM調(diào)優(yōu)的時候需要避免JVM頻繁發(fā)生full gc。full gc的速度比young gc要慢10倍。

分代收集算法    通過上述的分析呢,就知道了對于堆中的新生代和老年代會采用不同的垃圾回收算法來回收“死亡”的對象,這種分代回收對象的方法稱為“分代收集算法”。這個分代收集算法根據(jù)各個年代的特點(diǎn)采用適當(dāng)?shù)氖占惴āT谛律校看蜧C的時候都發(fā)現(xiàn)大批的對象死去,只有少量存活,自然選用復(fù)制算法;而對于老年代這種存活率高、沒有額外擔(dān)保空間的,就必須使用“標(biāo)記-清除算法”或者“標(biāo)記-整理算法“了。    GC設(shè)計的理論基礎(chǔ)就是這些了,其實(shí)原理還是比較容易理解的。GC的具體實(shí)現(xiàn)就是垃圾收集器,目前尚沒有一個垃圾收集器是完美的,需要配合使用。下面插上一副堆內(nèi)存劃分圖。注:本文寫的比較片面,如果想更深入了解,推薦這篇博文:http://jbutton.iteye.com/blog/1569746


發(fā)表評論 共有條評論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 久在线观看福利视频69 | 久久精品视频在线免费观看 | 男男啪羞羞视频网站 | 日本免费一区二区三区四区 | 一本色道久久久888 香蕉视频99 | 牛牛碰在线 | 超碰97最新 | www亚洲成人 | 91看片淫黄大片欧美看国产片 | 男女视频免费看 | 一级片九九 | 国产精品一区2区3区 | 精品999www| 国产黄色毛片 | 亚洲一级成人 | 精品国产一区二区三区四 | 日本中文字幕久久 | 欧美精品久久久久久久久久 | 国产免费一级 | 做羞羞视频 | 欧美一页| 欧美乱淫 | 久久精品亚洲成在人线av网址 | avav在线播放 | 国产自在自线午夜精品视频在 | 成人午夜免费网站 | 久久无| 国产一级二级毛片 | 黄色免费入口 | 国产亚洲在线 | 欧美激情性色生活片在线观看 | 国产高潮好爽好大受不了了 | 国产91在线高潮白浆在线观看 | 奇米888一区二区三区 | 欧美a视频| 免费激情网站 | 亚洲成人精品久久 | 羞羞视频免费视频欧美 | av电影免费在线 | 欧美成人精品不卡视频在线观看 | 黄色大片网 |