jvm垃圾回收算法

[TOC]java

引用計數

  1. 通常來講,每一個對象對應一個計數器,建立對象時,將其計數器置0算法

  2. 當對象唄賦予任意變量時,引用計數器每次加1.優化

  3. 引用變量出了做用域後,該引用變量所引用的對象的計數器減1.spa

  4. 一旦引用計數器爲0,對象就知足垃圾收集的條件
    <br/>設計

  • 優勢:基於引用計數器的垃圾收集器運行較快,不會長時間中斷程序執行,適宜必須實時運行的程序。指針

  • 缺點:引用計數器增長了程序執行的開銷,由於每次對象賦予給新的變量,計數器加1,而每次引用變量出了 做用域後,該引用的對象的計數器減1code

還沒法解決環形引用對象

Class A(){
    public B b = null;
}
Class B(){
    public A a = null;
}
public void test(){
    A a = new A();
    B b = new B();
    a.b=b;
    b.a = a
}

標記-清除

  1. mutator:除了垃圾收集器以外的部分,好比說咱們的應用程序自己。職責(分配內存),read(從內存中讀取內容),write(將內容寫入內存)進程

  2. collector:回收再也不使用的內存,供mutator 使用new分配內存圖片

  3. mutator roots:通常指的是分配在堆內存以外,能夠直接被mutator直接訪問到的對象

  • 標記階段:collector從mutator根對象開始進行遍歷,能被mutator roots訪問到的對象都打上一個標識,記錄爲可達對象

  • 清除階段:collector對內存從頭至尾進行線性變量,若是某個對象沒有標記爲可達對象,就將其回收
    圖片描述

從上圖咱們能夠看到,在Mark階段,從根對象1能夠訪問到B對象,從B對象又能夠訪問到E對象,因此B,E對象都是可達的。同理,F,G,J,K也都是可達對象。到了Sweep階段,全部非可達對象都會被collector回收。同時,Collector在進行標記和清除階段時會將整個應用程序暫停(mutator),等待標記清除結束後纔會恢復應用程序的運行
<br/>

  • 缺點:標記-清除算法的比較大的缺點就是垃圾收集後有可能會形成大量的內存碎片,像上面的圖片所示,垃圾收集後內存中存在三個內存碎片,假設一個方格表明1個單位的內存,若是有一個對象須要佔用3個內存單位的話,那麼就會致使Mutator一直處於暫停狀態,而Collector一直在嘗試進行垃圾收集,直到Out of Memory。暫停整個應用


複製收集

將堆內存分紅兩個相同空間,從根(相似於前面的有向圖起始頂點)開始訪問每個關聯的可達對象,將空間A的所有可達對象複製到空間B,而後一次性回收空間A。對於該算法而言,由於只需訪問全部的可達對象,將全部的可達對象複製走以後就直接回收整個空間,徹底不用理會不可達對象,因此遍歷空間的成本較小,但須要巨大的複製成本和較多的內存。
圖片描述
<br/>

  • 缺點:須要兩倍內存空間。

  • 優勢:不會出現碎片


標記-壓縮

此算法結合了「標記-清除」和「複製」兩個算法的優勢。也是分兩階段,第一階段從根節點開始標記全部被引用對象,第二階段遍歷整個堆,把清除未標記對象而且把存活對象「壓縮」到堆的其中一塊,按順序排放。此算法避免了「標記-清除」的碎片問題,同時也避免了「複製」算法的空間問題。
<br/>

  • 優勢:避免了「標記-清除」的碎片問題,同時也避免了「複製」算法的空間問題。

  • 缺點:暫停整個應用


增量收集

基礎還是傳統的標記-清除和複製算法。設計一個多進程的運行環境,好比用一個進程執行垃圾收集工做,另外一個進程執行程序代碼。這樣一來,垃圾收集工做看上去就彷彿是在後臺悄悄完成的,不會打斷程序代碼的運行。
<br/>

  • 缺點:垃圾收集器在第一階段中辛辛苦苦標記出的結果極可能被另外一個進程中的內存操做代碼修改得面目全非,以致於第二階段的工做沒有辦法開展。---------解決辦法: 優化算法

  • 優勢:垃圾收集工做看上去就彷彿是在後臺悄悄完成的,不會打斷程序代碼的運行。


分代收集

圖片描述

  • 新生代
    新生代包括兩個區:Eden區和Survivor區,其中Survivor區通常也分紅兩塊,簡稱Survivor1 Space 和 Survivor2 Space (或者From Space 和 To Space)。新生代一般存活時間較短,所以基於標記清除複製算法來進行回收,掃描出存活的對象,並複製到一塊新的徹底未使用的空間中,對應於新生代,就是在Eden和From或To之間copy。新生代採用空閒指針的方式來控制GC觸發,指針保持最後一個分配的對象在新生代區間的位置,當有新的對象要分配內存時,用於檢查空間是否足夠,不夠就觸發GC。當連續分配對象時,對象會逐漸從eden到Survior,最後到舊生代。

回收機制:複製回收

  • 老年代
    在垃圾回收屢次,若是對象仍然存活,而且新生代的空間不夠,則對象會存放在老年代。

在老年代採用的是 標記清除壓縮算法。由於老年代的對象通常存活時間比較長,每次標記清除以後,會有不少的零碎空間,這個就是所謂的浮動垃圾。當老年代的零碎空間不足以分配一個大的對象的時候,就會採用壓縮算法。在壓縮的時候,應用須要暫停。

回收機制:標記-壓縮

  • 持久代
    這部分空間主要存放java方法區的數據以及啓動類加載器加載的對象。這一部分對象一般不會被回收。因此持久代空間在默認的狀況下是不會被垃圾回收的。

回收機制:不會被回收

首先想eden區申請分配空間,若是空間夠,就直接進行分配,不然進行一次Minor GC。minor GC 首先會對Eden區的對象進行標記,標記出來存活的對象。而後把存活的對象copy到From空間。若是From空間足夠,則回收eden區可回收的對象。若是from內存空間不夠,則把From空間存活的對象複製到To區,若是TO區的內存空間也不夠的話,則把To區存活的對象複製到老年代。若是老年代空間也不夠(或者達到觸發老年年垃圾回收條件的話)則觸發一次full GC。Minor GC通常狀況下,當新對象生成,而且在Eden申請空間失敗時,就好觸發Minor GC,堆Eden區域進行GC,清除非存活對象,而且把尚且存活的對象移動到Survivor區。而後整理Survivor的兩個區。Full GC對整個堆進行整理,包括Young、Tenured和Perm。Full GC比Scavenge GC要慢,所以應該儘量減小Full GC。有以下緣由可能致使Full GC

相關文章
相關標籤/搜索