GC垃圾回收(1)- 回收算法與分代模型

1. 什么是garbage垃圾?

沒有任何引用指向的一個對象或者多個對象(循環(huán)引用),就是垃圾

1.1 Java與C++對于垃圾處理的區(qū)別

  • Java
    GC處理垃圾
    開發(fā)效率高,執(zhí)行效率低
  • C++
    手動處理垃圾
    忘記回收 -> 會導(dǎo)致內(nèi)存泄漏
    回收多次 -> 會造成非法訪問
    開發(fā)效率低,執(zhí)行效率高

2. 怎么定位垃圾

Java堆中存放著幾乎所有的對象實例,垃圾回收器在堆進行垃圾回收前,首先要判斷這些對象那些還存活,哪些成為了”垃圾“。定位“垃圾”有如下算法:
(1)引用計數(shù)法(ReferenceCount)
(2)根可達算法(RootSearching)

2.1 引用計數(shù)法(ReferenceCount)

引用計數(shù)法描述的算法為:給對象增加一個引用計數(shù)器,每當(dāng)有一個地方引用它時,計數(shù)器就+1;當(dāng)引用失效時,計數(shù)器就-1;任何時刻計數(shù)器為0的對象就是不能再被使用的,成為“垃圾”。

引用計數(shù)法實現(xiàn)簡單,判定效率也比較高,在大部分情況下都是一個比較好的算法。比如Python語言就是采用的引用計數(shù)法來進行內(nèi)存管理的。

但是,在主流的JVM中沒有選用引用計數(shù)法來管理內(nèi)存,最主要的原因是 引用計數(shù)法無法解決對象的循環(huán)引用問題。

2.2 根可達算法(RootSearching)

Java并不采用引用計數(shù)法來判斷對象是否已成為“垃圾”,而采用“根可達算法”來判斷對象是否存活(同樣采用此法的還有C#、Lisp-最早的一門采用動態(tài)內(nèi)存分配的語言)。

此算法的核心思想:通過一系列稱為“GC roots”的對象作為起始點,從這些節(jié)點開始向下搜索,搜索走過的路徑稱為“引用鏈”,當(dāng)一個對象到 GC roots 沒有任何的引用鏈相連時(從 GC roots 到這個對象不可達)時,證明此對象不可用,是垃圾。

哪些實例可以作為GC roots?

  • JVM stack Java 虛擬機棧(棧幀中的本地變量表)引用的對象
  • native method stack 本地方法棧中引用的對象
  • run-time constant 運行時常量池
  • static references in method area 方法區(qū)中靜態(tài)變量引用的對象

如圖:


Root Searching.png

3. GC Algorithms 垃圾回收算法

對存活對象和垃圾對象進行區(qū)分之后就需要進行回收了,垃圾回收算法有以下3種:

  • Mark-Sweep (標(biāo)記清除)
  • Copying(復(fù)制)
  • Mark-Compact(標(biāo)記壓縮 或 標(biāo)記整理)

3.1 Mark-Sweep (標(biāo)記清除)

“標(biāo)記清除”算法是最基礎(chǔ)的回收算法。算法分為標(biāo)記和清除兩個階段:首先標(biāo)記出所有需要回收的對象,在標(biāo)記完成后統(tǒng)一回收所有被標(biāo)記的對象(標(biāo)記過程使用根可達算法)。后續(xù)的回收算法都是基于這種思路并對其不足加以改進而已。

“標(biāo)記清除”算法的不足主要有兩個:

  • 效率問題:標(biāo)記和清除這兩個過程的效率都不高
  • 空間問題:標(biāo)記清除后會產(chǎn)生大量不連續(xù)的內(nèi)存碎片,空間碎片太多可能會導(dǎo)致以后在程序運行中需要分配較大對象時,無法找到足夠連續(xù)內(nèi)存而不得不提前觸發(fā)另一次垃圾回收。
標(biāo)記清除算法.png

3.2 Copying(復(fù)制)

“復(fù)制”算法是為了解決“標(biāo)記清除”的效率問題。它將可用內(nèi)存按容量劃分為大小相等的兩塊,每次只使用其中一塊。當(dāng)這塊內(nèi)存需要進行垃圾回收時,會將此區(qū)域還存活著的對象復(fù)制到另一塊上面,然后再把已經(jīng)使用過的內(nèi)存區(qū)域一次清理掉。這樣做的好處是每次都是對整個半?yún)^(qū)進行內(nèi)存回收,內(nèi)存分配時也就不需要考慮內(nèi)存碎片等的復(fù)雜情況,只需要移動堆頂指針,按順序分配即可。

此算法實現(xiàn)簡單,運行高效;不足之處是占用內(nèi)存多。


復(fù)制算法.png

3.3 Mark-Compact(標(biāo)記壓縮)

復(fù)制回收算法在對象存活率較高時會進行比較多的復(fù)制操作,效率會變低。因此在老年代一般不能使用復(fù)制算法。

針對老年代的特點,提出了一種“標(biāo)記整理算法”。標(biāo)記過程仍與“標(biāo)記清除”過程一致,但后續(xù)步驟不是直接對可回收對象進行清理,而是讓所有存活對象向一端移動,然后直接清理掉邊界以外的內(nèi)存。

好處是能整理出連續(xù)的內(nèi)存,不會產(chǎn)生碎片,方便對象分配,也不會將可用內(nèi)存減半;
不足:要掃描兩次,還要移動對象,效率偏低。

標(biāo)記壓縮(整理)算法.png

4. JVM邏輯分代模型(用于分代垃圾回收算法)

這是部分垃圾回收器使用的模型

除Epsilon ZGC Shenandoah之外的GC都是使用邏輯分代模型
G1是邏輯分代,物理不分代
除此之外不僅邏輯分代,而且物理分代

分代垃圾回收算法,并沒有新思想,只是根據(jù)對象存活周期的不同將內(nèi)存劃分為幾塊。
一般是把Java堆分為新生代和老年代。在新生代中,每次垃圾回收都有大批對象被回收,只有少量存活,因此采用復(fù)制算法;而老年代中對象存活率高、沒有額外空間對它進行分配擔(dān)保,就必須采用"標(biāo)記清理"或者"標(biāo)記壓縮"算法。

現(xiàn)在的商用虛擬機(包括HotSpot)都是采用這種收集算法來回收新生代
新生代中98%的對象都是"朝生夕死"的,所以并不需要按照1 : 1的比例來劃分內(nèi)存空間,而是將內(nèi)存(新生代內(nèi)存)分為一塊較大的Eden(伊甸園)空間和兩塊較小的Survivor(幸存者)空間,每次使用Eden和其中一塊Survivor(兩個Survivor區(qū)域一個稱為From區(qū),另一個稱為To區(qū)域)。當(dāng)回收時,將Eden和Survivor中還存活的對象一次性復(fù)制到另一塊Survivor空間上,最后清理掉Eden和剛才用過的Survivor空間。
當(dāng)Survivor空間不夠用時,需要依賴其他內(nèi)存(老年代)進行分配擔(dān)保。
HotSpot默認(rèn)Eden與Survivor的大小比例是8 : 1,也就是說Eden:Survivor From : Survivor To = 8:1:1。所以每次新生代可用內(nèi)存空間為整個新生代容量的90%,而剩下的10%用來存放回收后存活的對象。


堆內(nèi)存邏輯分區(qū).png

4.1 對象從出生到消亡

對象會首先分配到stack棧中,如果棧中分配不下了,會分配到Eden區(qū)中。Eden區(qū)進行垃圾回收后,存活對象被復(fù)制到s1區(qū),清空Eden區(qū)。當(dāng)觸發(fā)第二次垃圾回收時,將Eden區(qū)、s1區(qū)存活對象復(fù)制到s2區(qū),清空eden和s1......

如此循環(huán)進行,對象被復(fù)制一次年齡加1,當(dāng)Survivor區(qū)對象的復(fù)制年齡超過限制時,進入Old區(qū)。

通過參數(shù):-XX:MaxTenuringThreshold配置對象被復(fù)制的最大次數(shù)。

對象什么時候進入老年代?
對象頭mark word有4位是記錄對象年齡的,4位二進制最大是15,也就是說年齡最大為15

  • 超過XX:MaxTenuringThreshold指定次數(shù)(YGC)
    • Parallel Scavenge回收器 15
    • CMS回收器 6
    • G1回收器 15
  • 動態(tài)年齡
    • s1 --> s2 超過50%
    • 把年齡最大的放入old

4.1.1 對象的分配

  • 棧上分配

    • 線程私有小對象
    • 無逃逸(只在某代碼段中使用)
    • 支持標(biāo)量替換(用普通類型屬性代替對象)
    • 優(yōu)化時無需調(diào)整
  • 線程本地分配TLAB(Thread Local Allocation Buffer)

    • 占用eden,默認(rèn)1%(為避免對象分配時進行空間爭用線程同步影響效率,每個線程獨占eden區(qū)1%的空間)
    • 多線程時不用競爭eden就可以申請空間,提高效率
    • 小對象
    • 優(yōu)化時無需調(diào)整
  • 老年代

    • 大對象

對象分配過程總結(jié)

對象分配過程總結(jié).png

  • eden

4.2 GC概念

GC概念.png
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容