四、Java基礎之GC垃圾回收的歷史


垃圾回收思路:

  • 發現無用信息對象
  • 回收被無用對象佔用的內存空間,使該空間可被程序再次使用。

關鍵詞聯想:(類型)堆和非堆,(人類成長)伊甸園-倖存者樂園-養老園 (垃圾分類) 可達性 和標記 (垃圾清理)標記清理 空間to 複製 All 標記整理 more 分代回收

1、JVM的內存結構

Jvm(Java虛擬機)主要管理兩種類型內存:堆和非堆。 ##

1.1、堆-運行時數據區域

所有類實例和數組的內存均從此處分配,由垃圾回收器的自動內存管理系統回收

  • 堆內新生代和老年代。比例爲1:2。
  • 老年代:主要存放應用程序中生命週期長的存活對象。
  • 新生代:一個Eden區和兩個Survivor區,比例爲8:1:1 Eden區存放新生的對象。Survivor存放每次垃圾回收後存活的對象

1.2、非堆:

JVM留給自己用的,包含方法區、JVM內部處理或優化所需的內存(如 JIT Compiler,Just-in-time Compiler,即時編譯後的代碼緩存)、每個類結構(如運行時常數池、字段和方法數據)以及方法和構造方法的代碼。

1.3、形象記憶

來一張圖有助於記憶

伊甸園(Eden):這是對象最初誕生的區域,並且對大多數對象來說,這裏是它們唯一存在過的區域。

倖存者樂園(Survivor):從伊甸園倖存下來的對象會被挪到這裏。

終身頤養園(Old Generation):這是足夠老的倖存對象的歸宿。年輕代收集(Minor-GC)過程是不會觸及這個地方的。當年輕代收集不能把對象放進終身頤養園時,就會觸發一次完全收集(Major-GC),這裏可能還會牽扯到壓縮,以便爲大對象騰出足夠的空間。

ok,在看看垃圾收集的思路第一條發現無用信息對象

2、 可回收對象的判定

  • 引用計數算法
  • 可達性分析算法(根搜索算法)

2.1、引用計數算法

堆中每個對象實例都有一個引用計數。

當一個對象被創建時,且將該對象實例分配給一個變量,該變量計數設置爲1。

當變量被賦值給其他對象時,計數加1(a = b,則b引用的對象實例的計數器+1)

但當一個對象實例的某個引用超過了生命週期或者被設置爲一個新值時,對象實例的引用計數器減1。

任何引用計數器爲0的對象實例可以被當作垃圾收集。

當一個對象實例被垃圾收集時,它引用的任何對象實例的引用計數器減1

優缺點

  • 優點: 簡單,高效
    引用計數收集器可以很快的執行,交織在程序運行中。對程序需要不被長時間打斷的實時環境比較有利。
  • 缺點: 無法檢測出循環引用
    如父對象有一個對子對象的引用,子對象反過來引用父對象。這樣,他們的引用計數永遠不可能爲0.
public class Main {
    public static void main(String[] args) {
        MyObject object1 = new MyObject();
        MyObject object2 = new MyObject();

        object1.object = object2;
        object2.object = object1;

        object1 = null;
        object2 = null;
    }
}

最後面兩句將object1和object2賦值爲null,也就是說object1和object2指向的對象已經不可能再被訪問,但是由於它們互相引用對方,導致它們的引用計數器都不爲0,那麼垃圾收集器就永遠不會回收它們。

2.2、可達性分析算法(根搜索算法)

這個算法比較像樹,從根節點出發尋找下一個節點(引用),如果無法被尋找的節點被,清理掉!他的出現解決了循環引用的問題

java中可作爲GC Root的對象

  • 虛擬機棧中引用的對象(本地變量表)
  • 方法區中靜態屬性引用的對象
  • 方法區中常量引用的對象
  • 本地方法棧中引用的對象(Native對象)

3. 垃圾回收算法

3.1、標記清除算法

過程
分標記階段,清除階段,其實標記階段應該就是用了可達性分析算法,如果可達就標記,後面清除未標記的


優缺點:
優點:簡單,容易實現
缺點:容易產生內存碎片,碎片太多可能會導致後續過程中需要爲大對象分配空間時無法找到足夠的空間而提前觸發新的一次垃圾收集動作。

3.2、 複製算法 (Copying)

它的出現就是解決空間利用率不足的問題,所以第一步分區域
活動區:
在任意時間點,所有動態分配的對象都只能分配在活動區
空閒區:
當活動區快接近full的時候,觸發GC,將存活的從活動區轉移到空閒區,當然這個時候他們兩的意義就交換了

優缺點

  • 優點:實現簡單,運行高效且不容易產生內存碎片
  • 缺點:浪費內存
    總結
    適用於存活對象很少。回收對象多,畢竟需要copy嘛,存活越少效率越高

3.3、 標記整理算法 (Mark-Compact)

他纔是標記清理算法的升級版,不用多說了,就是多了一個步驟整理
優缺點

  • 優點: 標記/整理算法不僅可以彌補標記/清除算法當中,內存區域分散的缺點,也消除了複製算法當中,內存減半的高額代價
  • 缺點: 低效
    總結
    適用用於存活對象多,回收對象少,回收對象一少,這樣整理起來效率也高

3.4、分代回收算法(Generational Collector)

他則是copy+Mark-Compact整合版

你先想想之前兩個回收算法的總結,就會很快明白分代回收的原理

3.4.1、 原理(過程)

所有新生成的對象首先都是放在年輕代的。年輕代的目標就是儘可能快速的收集掉那些生命週期短的對象。

  • 分配對象時,保存在Eden區
  • Eden區滿,觸發GC(Minor GC), 將Eden區存活對象複製到一個survivor0區,然後清空Eden區
  • 當這個survivor0區也存放滿了時,則將eden區和survivor0區存活對象複製到另一個survivor1區,然後清空eden和這個survivor0區
  • 此時survivor0區是空的,然後將survivor0區和survivor1區交換,即保持survivor1區爲空, 如此往復
  • 當survivor1區不足以存放 eden和survivor0的存活對象時,就將存活對象直接存放到老年代。若是老年代也滿了就會觸發一次Full GC,也就是新生代、老年代都進行回收

參考深入理解Java垃圾回收機制

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章