欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

JVM知識(shí)總結(jié)之垃圾收集算法

 更新時(shí)間:2021年06月03日 15:57:00   作者:AlaGeek  
本博客為讀書筆記,讀的是《深入理解Java虛擬機(jī)》一書,在看這個(gè)書的時(shí)候,最大的一個(gè)感受便是“當(dāng)初怎么就沒有好好學(xué)習(xí)操作系統(tǒng)呢,不然也不會(huì)有這么多看的云里霧里的地方了”,不過(guò)那都是過(guò)去的事了,學(xué)習(xí)最好的時(shí)刻便是現(xiàn)在,需要的朋友可以參考下

一、什么是垃圾

本文要講的是垃圾收集算法,那么首先要確定的問題就是什么是垃圾,也就是哪些對(duì)象是要被回收的,對(duì)此有兩種判斷方式:

1.1 引用計(jì)數(shù)算法

什么樣的對(duì)象是要被回收的,很明顯,沒有被引用的對(duì)象才要被回收。因此在對(duì)象中加一個(gè)引用計(jì)數(shù)器,當(dāng)有一個(gè)對(duì)象引用該對(duì)象的時(shí)候,計(jì)數(shù)器就加一,當(dāng)引用結(jié)束后,計(jì)數(shù)器就減一,當(dāng)計(jì)數(shù)器為0的時(shí)候,對(duì)象就可以被回收了。

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

  • 原理簡(jiǎn)單
  • 判斷效率高

1.1.2 缺點(diǎn)

  • 需要花費(fèi)額外的內(nèi)存空間(引用計(jì)數(shù)器)
  • 無(wú)法回收相互循環(huán)引用的對(duì)象:比如有對(duì)象A和對(duì)象B,A引用B,B引用A,兩對(duì)象的引用計(jì)數(shù)器都為1,理論上來(lái)說(shuō),沒有其他對(duì)象能夠引用到A和B了,因此這兩個(gè)對(duì)象應(yīng)該被回收,然后按照引用計(jì)數(shù)算法的判斷,這兩個(gè)對(duì)象無(wú)法被回收。(要克服這個(gè)缺點(diǎn),需要在代碼中做很多特殊處理)

1.2 可達(dá)性分析算法

因?yàn)橐糜?jì)數(shù)算法的缺陷,各大主流的商用程序語(yǔ)言都采用可達(dá)性分析算法來(lái)判斷對(duì)象是否需要被回收??蛇_(dá)性顧名思義,是指對(duì)象跟對(duì)象之間有引用關(guān)系,此處有兩種引用關(guān)系:

  • 對(duì)象A引用對(duì)象B,則稱對(duì)象A到對(duì)象B可達(dá)
  • 對(duì)象A引用對(duì)象B,對(duì)象B引用對(duì)象C,對(duì)象A可以通過(guò)若干個(gè)對(duì)象(此處為對(duì)象B)引用到對(duì)象C,則稱對(duì)象A到對(duì)象C可達(dá)。

要判斷一個(gè)對(duì)象是否可達(dá),首先要有一個(gè)根對(duì)象,在Java中有一系列被稱為“GC Roots”的根對(duì)象作為起始節(jié)點(diǎn)集,任何從“GC Roots”不可達(dá)的對(duì)象都是需要被垃圾收集器回收的垃圾。

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

可以有效解決引用計(jì)數(shù)算法的相互循環(huán)引用問題

 二、什么是引用

在討論什么是垃圾的時(shí)候,多次提到引用一詞,那么什么是引用呢?

2.1 JDK1.2以前

按照書中的說(shuō)法,在JDK1.2以前,引用的意思是:如果reference類型的數(shù)據(jù)中存儲(chǔ)的數(shù)值代表的是另外一塊內(nèi)存的起始地址,就稱該reference數(shù)據(jù)是代表某塊內(nèi)存、某個(gè)對(duì)象的引用。
在這種定義下可以發(fā)現(xiàn),對(duì)于一個(gè)對(duì)象來(lái)說(shuō),就只有未被引用和被引用兩種狀態(tài)了,但其實(shí)可以發(fā)現(xiàn),在實(shí)際應(yīng)用中,并不是一定要把對(duì)象回收掉的,書中有個(gè)詞就很貼切,“食之無(wú)味,棄之可惜”,我們想要的是當(dāng)內(nèi)存空間足夠的時(shí)候,把這部分本該回收的對(duì)象留著不回收,當(dāng)內(nèi)存不夠的時(shí)候,就將其回收。

2.2 JDK1.2之后

因此在JDK1.2之后,引用的概念就擴(kuò)張到了以下四種:

  • 強(qiáng)引用:指?jìng)鹘y(tǒng)意義上的引用,有強(qiáng)引用的對(duì)象是肯定不被回收的;
  • 軟引用:用于描述一些還有用,但非必須的對(duì)象,當(dāng)要發(fā)生內(nèi)存溢出的時(shí)候,就會(huì)回收軟引用對(duì)象;
  • 弱引用:用于描述非必須對(duì)象,強(qiáng)度比軟引用弱一點(diǎn),當(dāng)垃圾收集器開始工作,無(wú)論內(nèi)存夠不夠都會(huì)回收弱引用對(duì)象;
  • 虛引用:虛引用意思就是這個(gè)引用跟沒有一樣,對(duì)對(duì)象完全沒有印象,其存在的唯一作用就是在對(duì)象被垃圾收集器回收時(shí)能收到一個(gè)系統(tǒng)通知。

三、垃圾判斷全流程

按照書中所述,我畫了個(gè)流程圖,如下:

垃圾判斷全流程

一個(gè)對(duì)象在被回收前,需要進(jìn)行兩次標(biāo)記,第一次進(jìn)行可達(dá)性分析后,對(duì)象被垃圾收集器認(rèn)為是垃圾,則對(duì)對(duì)象進(jìn)行第一次標(biāo)記,然后垃圾收集器會(huì)給予對(duì)象一次自救的機(jī)會(huì),不然就沒必要兩次標(biāo)記了,一次標(biāo)記直接回收就好了。
我們都知道對(duì)象有個(gè)finalize()方法,自救的機(jī)會(huì)就在這個(gè)方法中,當(dāng)?shù)谝淮螛?biāo)記后,垃圾收集器會(huì)對(duì)對(duì)象做一次篩選,篩選條件是要不要執(zhí)行對(duì)象的finalize()方法,如果開發(fā)者未對(duì)finalize()方法進(jìn)行覆寫或者虛擬機(jī)已經(jīng)執(zhí)行過(guò)該對(duì)象的finalize()方法了,那么自然就不用再執(zhí)行了,反之則需要執(zhí)行。
將篩選出來(lái)的需要執(zhí)行finalize()方法的對(duì)象放入一個(gè)特定的隊(duì)列中,由虛擬機(jī)統(tǒng)一執(zhí)行,如果finalize()方法中使得對(duì)象被別的對(duì)象引用了,導(dǎo)致可達(dá)性分析認(rèn)為對(duì)象是可用的,那么自救就成功了。
根據(jù)篩選的條件可以知道,對(duì)象的自救機(jī)會(huì)在整個(gè)程序中只有一次,因?yàn)閒inalize()方法只會(huì)被執(zhí)行一次。
需要注意的是,官方明確申明不推薦使用finalize()方法,因?yàn)槭褂盟牟淮_定性太大。對(duì)于資源清理等操作,try…catch語(yǔ)法可以做的更好。

四、垃圾收集算法

大多數(shù)虛擬機(jī)的垃圾收集都采用了分代收集的形式,這是因?yàn)槿龡l經(jīng)驗(yàn)法則:

  • 弱分代假說(shuō):絕大多數(shù)對(duì)象都是朝生夕死的;
  • 強(qiáng)分代假說(shuō):熬過(guò)越多次垃圾收集過(guò)程的對(duì)象就越難以消亡
  • 跨代引用假說(shuō):跨代引用相對(duì)于同代引用來(lái)說(shuō)僅占極少數(shù)

因?yàn)閷?duì)象的生存周期是不一樣的,所以我們不能對(duì)所有對(duì)象采用同一種垃圾收集算法,采用分代收集,將有共性的對(duì)象放在一個(gè)集合里,會(huì)大大地提高垃圾收集效率。
按照上述經(jīng)驗(yàn)法則,可以將堆內(nèi)存分為兩代:

  • 新生代:對(duì)應(yīng)弱分代假說(shuō)
  • 老年代:對(duì)應(yīng)強(qiáng)分代假說(shuō)

下面分別介紹三種垃圾收集算法:

4.1 標(biāo)記-清除算法

標(biāo)記-清除算法是最基礎(chǔ)的垃圾收集算法,顧名思義,標(biāo)記就是判斷對(duì)象是否是垃圾,也就是前面第四節(jié)講到的內(nèi)容,清除就是統(tǒng)一回收垃圾,該算法有兩種執(zhí)行過(guò)程:

  • 標(biāo)記所有需要被回收的對(duì)象,統(tǒng)一回收所有標(biāo)記的對(duì)象;
  • 標(biāo)記所有存活對(duì)象,統(tǒng)一回收所有未被標(biāo)記的對(duì)象。

標(biāo)記-清除算法示意圖:

標(biāo)記-清除算法

標(biāo)記-清除算法有兩大缺點(diǎn):

  • 執(zhí)行效率不穩(wěn)定:標(biāo)記和清除兩個(gè)過(guò)程的執(zhí)行效率隨對(duì)象數(shù)量的增加而降低
  • 內(nèi)存空間碎片化:執(zhí)行完標(biāo)記和清除后,會(huì)產(chǎn)生大量的不連續(xù)的內(nèi)存碎片,當(dāng)分配大對(duì)象的時(shí)候,如果找不到足夠的連續(xù)內(nèi)存,那么會(huì)提前觸發(fā)下一次垃圾收集。

4.2 標(biāo)記-復(fù)制算法

基于標(biāo)記-清除算法的缺點(diǎn),標(biāo)記-復(fù)制算法將內(nèi)存空間一分為二,兩塊內(nèi)存空間等大,每次只使用其中一塊內(nèi)存空間,當(dāng)這一塊內(nèi)存空間用完了,就把存活的對(duì)象復(fù)制到另一塊內(nèi)存空間中,然后一次性清理所有已使用的內(nèi)存空間。

標(biāo)記-復(fù)制算法示意圖:

標(biāo)記-復(fù)制算法

標(biāo)記-復(fù)制算法解決了標(biāo)記-清除算法面對(duì)大量可回收對(duì)象場(chǎng)景下的不足之處,面對(duì)這種情況,標(biāo)記-復(fù)制算法只需要將內(nèi)存空間中的存活對(duì)象復(fù)制到另一半內(nèi)存空間中,可以有效解決內(nèi)存碎片的問題,在給對(duì)象分配內(nèi)存的時(shí)候,只需要移動(dòng)堆頂指針按順序分配即可,不過(guò)這個(gè)算法也有缺點(diǎn):

  • 面對(duì)大量不可回收對(duì)象的時(shí)候,會(huì)產(chǎn)生大量?jī)?nèi)存間對(duì)象復(fù)制的開銷;
  • 原先的內(nèi)存空間縮小了一半,會(huì)造成嚴(yán)重的空間浪費(fèi)

4.3 標(biāo)記-整理算法

標(biāo)記-復(fù)制算法不足以應(yīng)對(duì)有大量存活對(duì)象的場(chǎng)景,因此就有了標(biāo)記-整理算法,該算法的執(zhí)行流程如下:

  • 與其他算法一樣,首先對(duì)對(duì)象進(jìn)行標(biāo)記;
  • 將所有存活對(duì)象往內(nèi)存的一個(gè)方向移動(dòng);
  • 直接清理掉邊界以外的內(nèi)存

標(biāo)記-整理算法示意圖:

標(biāo)記-整理算法

標(biāo)記-整理算法同樣可以解決內(nèi)存碎片化問題,并且不會(huì)造成空間浪費(fèi),不過(guò)它也有缺點(diǎn):

在大量對(duì)象存活的情況下,移動(dòng)對(duì)象并更新引用也會(huì)花費(fèi)大量時(shí)間

4.4 應(yīng)用

不同的場(chǎng)景適用不同的垃圾收集算法,像標(biāo)記-復(fù)制算法就適用于存活對(duì)象少的情況下,也就是新生代區(qū)域,像標(biāo)記-整理算法就適用于存活對(duì)象多的情況下,也就是老年代。
這里有點(diǎn)需要注意的是,標(biāo)記-整理算法對(duì)于老年代來(lái)說(shuō)也不是完美的,在5.3節(jié)我們說(shuō)過(guò),在大量對(duì)象存活的情況下,移動(dòng)對(duì)象和更新引用也是要花費(fèi)大量時(shí)間的,不過(guò)算法這個(gè)東西吧,它比的是誰(shuí)更適合,對(duì)于標(biāo)記-復(fù)制算法來(lái)說(shuō),我把區(qū)域一分為二,如果大量對(duì)象存活,我要把對(duì)象全部復(fù)制到另一塊內(nèi)存區(qū)域,這個(gè)開銷不見得比標(biāo)記-整理算法少,并且它還有個(gè)缺點(diǎn)就是可用內(nèi)存一下子少了一半,這個(gè)問題在標(biāo)記-整理算法中是沒有的。也有的虛擬機(jī)采用標(biāo)記-清除算法標(biāo)記-整理算法協(xié)作的垃圾收集方案,沒有最適合,只有更適合。

4.5 優(yōu)化

前面講標(biāo)記-復(fù)制算法的時(shí)候說(shuō)到要把內(nèi)存區(qū)域等半分,這是在沒有規(guī)定場(chǎng)景的情況下,在新生代中采用該垃圾收集算法可以做更好的優(yōu)化。

眾所周知,新生代中的對(duì)象都是朝生夕死的,因此當(dāng)標(biāo)記完成后的存活對(duì)象肯定是少量的,根據(jù)這個(gè)現(xiàn)象,可以將內(nèi)存區(qū)域非等半分,比如說(shuō)9:1的分法,這里我們將90%的內(nèi)存區(qū)域稱為Eden空間,將10%的內(nèi)存區(qū)域稱為Survivor空間,一開始使用Eden空間的內(nèi)存,當(dāng)垃圾收集時(shí),將Eden空間的存活對(duì)象復(fù)制到Survivor空間中。
這里肯定有人要問了,那下一次使用Survivor空間不是就只有10%的內(nèi)存了嗎?

對(duì)的,所以這里有兩種解決方案:

  • 將存活對(duì)象從Eden空間復(fù)制到Survivor空間后,再?gòu)?strong>Survivor空間復(fù)制回Eden空間;
  • Eden空間再分離出一個(gè)Survivor空間,每次可使用的內(nèi)存為一個(gè)Eden空間一個(gè)Survivor空間,當(dāng)垃圾收集時(shí),將使用的內(nèi)存區(qū)域中的存活對(duì)象復(fù)制到另一個(gè)Survivor空間中,下一次的可用內(nèi)存則為Eden空間和這個(gè)Survivor空間,如此循環(huán)往復(fù)。

第二種方法就是大名鼎鼎的半?yún)^(qū)復(fù)制分代策略,現(xiàn)在叫Appel式回收,因?yàn)樘岢鲞@個(gè)策略的人叫Apple,目前很多虛擬機(jī)在新生代的垃圾收集算法中采用這個(gè)策略。

4.5.1 缺點(diǎn)

半?yún)^(qū)復(fù)制分代策略也是有缺點(diǎn)的,從上面的敘述中我們可以知道,Eden空間Survivor空間的內(nèi)存占比為8:1:1,如果當(dāng)垃圾收集后的存活對(duì)象所需要的內(nèi)存空間大于一個(gè)Survivor空間時(shí),那就難辦了。

4.5.2 補(bǔ)丁

既然Survivor空間的內(nèi)存不夠放存活對(duì)象了,那就去借內(nèi)存區(qū)域,這個(gè)借當(dāng)然不能跟Eden空間Survivor空間借,不然會(huì)影響到整個(gè)算法,增加算法的復(fù)雜度。新生代不能借,那就跟老年代借,這里就有一個(gè)所謂的內(nèi)存分配擔(dān)保,放不下的存活對(duì)象將直接通過(guò)分配擔(dān)保機(jī)制進(jìn)入到老年代中。有了這個(gè)“逃生門”一樣的設(shè)計(jì),這個(gè)策略才算是沒有漏洞。

五、寫在后面

幾個(gè)垃圾收集算法的圖是我直接截了書里面的圖,因?yàn)槲矣X得它講的很詳細(xì)了,第四節(jié)垃圾判斷過(guò)程在書中實(shí)際上是一長(zhǎng)串的代碼,看懂不難,不過(guò)我想畫個(gè)流程圖可能更清楚點(diǎn),這個(gè)流程圖是用plantUML畫出來(lái)的,這個(gè)工具可以用代碼畫出各種圖,功能強(qiáng)大,有興趣的可以百度搜搜,下面是這個(gè)流程圖的代碼:

@startuml
start
:對(duì)對(duì)象進(jìn)行可達(dá)性分析;
if (對(duì)象是否為垃圾?) then (是)
    :進(jìn)行第一次標(biāo)記;
    if (對(duì)象沒有覆蓋finalize()方法 或 finalize()方法已經(jīng)被虛擬機(jī)調(diào)用) then(是)
        :沒必要執(zhí)行對(duì)象的finalize()方法;
    else (否)
        :將對(duì)象放入隊(duì)列F-Queue中;
        :等待虛擬機(jī)的Finalizer線程執(zhí)行對(duì)象的finalize()方法;
        :執(zhí)行對(duì)象的finalize()方法;
    endif
    if (對(duì)象是否為垃圾?) then (是)
        :進(jìn)行第二次標(biāo)記;
        :垃圾回收;
    else (否)
        stop
    endif
else (否)
    stop
endif
stop
@enduml

到此這篇關(guān)于JVM知識(shí)總結(jié)之垃圾收集算法的文章就介紹到這了,更多相關(guān)JVM垃圾收集算法內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java垃圾回收之標(biāo)記清除算法詳解

    Java垃圾回收之標(biāo)記清除算法詳解

    今天小編就為大家分享一篇關(guān)于Java垃圾回收之標(biāo)記清除算法詳解,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧
    2018-10-10
  • 數(shù)組重排序(如何將所有奇數(shù)都放在所有偶數(shù)前面)的深入分析

    數(shù)組重排序(如何將所有奇數(shù)都放在所有偶數(shù)前面)的深入分析

    本篇文章是對(duì)數(shù)組重排序(如何將所有奇數(shù)都放在所有偶數(shù)前面)的方法進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下
    2013-06-06
  • java實(shí)現(xiàn)Redisson的基本使用

    java實(shí)現(xiàn)Redisson的基本使用

    Redisson是一個(gè)在Redis的基礎(chǔ)上實(shí)現(xiàn)的Java駐內(nèi)存數(shù)據(jù)網(wǎng)格客戶端,本文主要介紹了java實(shí)現(xiàn)Redisson的基本使用,具有一定的參考價(jià)值,感興趣的可以了解一下
    2023-12-12
  • mybatis-plus批量更新太慢該如何解決詳解

    mybatis-plus批量更新太慢該如何解決詳解

    這篇文章主要給大家介紹了關(guān)于mybatis-plus批量更新太慢該如何解決的相關(guān)資料,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2023-03-03
  • 出現(xiàn)log.info報(bào)紅的解決方案

    出現(xiàn)log.info報(bào)紅的解決方案

    這篇文章主要介紹了出現(xiàn)log.info報(bào)紅的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-06-06
  • Java groovy內(nèi)存回收測(cè)試步驟解析

    Java groovy內(nèi)存回收測(cè)試步驟解析

    這篇文章主要介紹了Java groovy內(nèi)存回收測(cè)試步驟解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-05-05
  • 通過(guò)HashMap原理詳解entrySet中的疑問

    通過(guò)HashMap原理詳解entrySet中的疑問

    這篇文章主要為大家介紹了通過(guò)HashMap原理詳解entrySet中的疑問,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-11-11
  • java中枚舉的詳細(xì)使用介紹

    java中枚舉的詳細(xì)使用介紹

    本篇文章介紹了,在java中枚舉的詳細(xì)使用。需要的朋友參考下
    2013-04-04
  • SpringBoot?集成短信和郵件的配置示例詳解

    SpringBoot?集成短信和郵件的配置示例詳解

    這篇文章主要介紹了SpringBoot?集成短信和郵件的相關(guān)知識(shí),項(xiàng)目中使用lombok插件和swagger依賴,無(wú)相關(guān)依賴的請(qǐng)自行修改,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下
    2022-04-04
  • idea中Stash與Unstash的使用及說(shuō)明

    idea中Stash與Unstash的使用及說(shuō)明

    這篇文章主要介紹了idea中Stash與Unstash的使用及說(shuō)明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-02-02

最新評(píng)論