JVM垃圾收集(GC)

參考文獻:周志明《深入理解Java虛擬機》第二版

因爲 Java 具有自動垃圾回收機制,所以,垃圾收集(Garbage Collection,GC),是 Java 技術的核心之一,是每一個 Java 程序員必知必備的一項技術,爲了深入掌握 Java 技術,我們必須學習 JVM 中的 GC 是如何實現的

GC 中包含三個主要的問題:

  • 哪些內存需要回收?
  • 什麼時候回收?
  • 如何回收?

我們對這三個問題逐一討論

哪些內存需要回收?

首先我們應該清楚,在 Java 內存運行時區域的各個部分中,程序計數器(PC)、虛擬機棧(VM Stack)、本地方法棧(NM Stack)三個區域隨線程而生,隨線程而滅,這幾個區域的內存分配和回收都具有確定性,不必過多考慮 GC 問題。而 Java 堆(Java Heap)和方法區(Method Area)就不一樣了,因爲,一個接口中的多個實現類需要的內存可能不一樣,一個方法中的多個分支需要的內存也可能不一樣,我們只有在程序運行中才能知道會創建哪些對象,所以這部分內存的分配與回收是動態的,我們需要在 Java 堆方法區 中研究 GC 問題

需要回收哪些內存,就是如何判定對象的“生”(正在被使用)或“死”(不會再被使用),有兩種判定方法:

引用計數法

這是一種簡單直接的判定方法,具體實現就是:給對象中添加一個引用計數器,每當它被引用時計數器就加 1 ;引用失效時,計數器就減 1,所以,計數器值爲 0 的對象,就是已經沒用的對象,可以被回收
這種方法實現簡單,判定高效,但是卻幾乎不被 JVM 使用,原因是它有一個致命缺陷:無法解決對象相互循環引用問題,比如,有兩個已經沒用的對象 objA 和 objB ,它們雖已經不會再被程序引用,但是它們相互引用 objA.instance = objB; objB.instance = objA; 這樣一來,它們的引用計數值都不爲 0,無法被清理掉,它們就會一直佔用空間

可達性分析法

這個判定對象“生死”算法是目前比較主流的 ,這個算法的基本思路是通過一系列的稱爲 GC Roots 的對象作爲起始點,然後從這些結點向下搜索,搜索過的路徑稱爲引用鏈,當一個對象到 GC Roots 沒有任何引用鏈相連(或是說從 GC Roots 不可達),就證明對象不可用,可以清除了
這裏寫圖片描述
如圖,Object 6, 7, 8 都是可清除的對象

那麼,GC Roots 具體都是什麼呢?它包括下面幾種:

  • 虛擬機棧(棧幀中的本地變量表)中引用的對象
  • 方法區中類靜態屬性引用的對象
  • 方法區中常量的引用對象
  • 本地方法棧中 JNI (即一般說的 Native 方法)引用的對象

ps. JVM規範中沒有要求方法區必須有 GC,因爲“性價比”低,過於複雜會影響性能,而且一些 JVM 將 Java 堆與方法區合爲一體,因此,在本文中不討論方法區的GC

什麼時候回收?

因爲 GC 主要發生在 Java 堆中,要回答這個問題就必須先明白 Java 堆的分區和 GC 的分類
Java 堆分爲 新生代老年代 兩個區,其中新生代又分爲 一個 Eden區和 兩個 Survior區(from Survior 和 to Survior),分出兩個 Survior區是因爲新生代回收算法採用 複製算法(後文會講解)
GC 分爲 新生代 GC(Minor GC)老年代 GC(Major GC / Full GC)
新創建的對象實例都會先進入 Eden區, 當 Eden區的空間不夠新對象存入時就會觸發 Minor GC,經歷一次 Minor GC 而未被清除的對象會被存入 Survior區,之後每經歷一次 Minor GC 未被清除,對象的“年齡”就會增長一歲,當歲數足夠時,就會進入老年代,當老年代滿時,就會觸發一次 Full GC
因爲 Java 對象大多都是朝生夕滅,所以 Minor GC 非常的頻繁,一般速度也比較快,Full GC 不常發生,而且速度要比 Minor GC 慢 10 倍以上

如何回收?

這個問題是十分重要的,它就是說 GC 算法是如何實現的,GC 算法有很多,我們在此討論最經典的幾個

標記-清除算法

這是最簡單基礎的 GC 算法,它分爲“標記”、“清除”兩個階段,就是簡單的標記出可回收對象,然後將其清除,不做其他操作
這裏寫圖片描述
這個方法雖簡單,但是它有兩個弊病:
① 效率不高
② 會產生大量空間碎片(如上圖)
大量的空間碎片會導致大對象進入時,無法找到足夠連續的內存空間,不得不觸發一次 Minor GC

複製算法

它將可用內存分爲大小相等的兩部分,每次只使用其中一塊,當這一塊用完了,就將還存活的對象複製到另一個上面,然後再把使用過的內存空間一次清理掉,這種算法簡單高效,不會產生內存碎片,但是代價是內存容量減小了一半,但是因爲它的高效快速,所以被應用在 Minor GC 中
這裏寫圖片描述

標記-整理算法

這個算法就是在標記-清除算法的基礎上加入了整理內存空間的功能,它不會產生內存碎片,也不會犧牲空間,但是因爲它要逐個整理導致回收速度緩慢,所以它被應用在老年代 Full GC 中
這裏寫圖片描述

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