GC的回收算法



基本概念

JVM中GC(Garbage Collector)的回收最常見的四種基本算法:Mark-Sweep(標記-清除),Copying(複製),Mark-Compact(標記-整理),Generational Collection(分代收集)(其實還有火車算法,但因爲很少使用在這裏就不進行討論)。這裏所列出的算法是有先後順序的,每一個算法是前面算法的優化。

其實在這一節不僅僅是要掌握GC常用的算法,還要理解是根據具體的儲存結構特點選擇使用適當的算法的思想。例如:在Java堆一部分區域會根據“新生代“和”老年代”一部分會採用複製算法,另一部分採用標記-清除算法。


下面是GC中常見的名詞性解釋:

  • collector:回收器(用於回收不在需要的內存)

  • mutator:更改器(對內存進行 分配,讀取,寫入)

  • Roots:根對象(可以由此處逐一訪問Java堆上的所有對象)

  • 可達對象:在根對象上進行遍歷,可被訪問的對象實例

    狀態
    可用不可達 易造成內存泄漏
    不可用不可達 回收的內容
    可用可達 正常

GC的原理:當一個對象實例的引用計數爲0(引用計數法)或者是沒能和Roots構成引用鏈的 即Roots根對象沒能遍歷到的對象(可達性分析法),則可能被GC進行回收。

本文是基於可達性分析法後的GC回收處理



根對象(Roots)

什麼是根對象?

  • 虛擬機棧的局部變量表引用的對象
  • 方法區的類變量與常量引用的對象(JDK1.8後變成Java元數據區的類變量與常量引用的對象)
  • 本地方法棧中JNI引用的對象

在這裏插入圖片描述

若不能和Roots對象構成引用鏈,那麼該內存區域將會被回收



標記

標記在GC中是一個很重要的概念,它是確認哪部分內存需要回收的唯一標識,所以在可達性分析法下的垃圾回收算法中都需要用到。

實際上,JVM在回收前對目標會做兩次標記。

流程如下:

1.第一次標記:對沒能和Roots構成引用鏈的對象進行標記,劃入可回收集合

2.對標記成員進行篩選

​ 2.1 覆蓋或者未調用過finalize()方法的成員,會進入需要執行finalize()方法的隊列

​ 2.2 對未覆蓋或者已經調用過finalize()方法的成員,已經是可回收狀態了

3.第二次標記:對隊列中的成員進行篩選

​ 3.1 執行完finalize()方法,便可以回收了

​ 3.2 若在執行finalize()方法過程中,與Roots對象又構成引用鏈,則該對象實例會被移出可回收集合。

下面開始討論幾種回收算法



Mark-Sweep算法(標記-清除 算法)

在這裏插入圖片描述

標記-清除算法是其他GC算法的基礎,它提出了一種在可達性分析後進行回收的方式。

1.標記:在遍歷Roots對象後,找到未能和Roots對象構成引用鏈的對象進行標記(兩次標記)

2.清除:兩次標記後,仍在可回收集合中的對象將被清除


優點:實現較簡單,開銷小

缺點:垃圾回收後,因爲沒有對空間進行整理,導致內存不連續變得碎片化。在這種情況下,如果需要分配一個較大的內存區域,可能會因沒有空間而導致OOM



Copying算法(複製算法)

在這裏插入圖片描述

複製算法是爲解決GC回收後內存碎片化而產生的。

調用後的過程如下:

1.將內存區域劃分爲兩個區域,每次使用一個區域(區域大小非1:1)

2.在進行標記-清除算法後,將仍然存活的對象逐一複製到另一部分內存上

3.清除另一部分的內存


優點: 解決了標記-清除方式的內存不連續問題

缺點:
1.空間複雜度高

​ 2.隨着對象存活率的升高,而時間複雜度變高


改良後的算法:針對新生代存活率低的特點,使用複製算法在這一區域上,並且使得複製算法的內存區域不用按照1:1分配。詳見下面的分代收集算法



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

在這裏插入圖片描述

標記-整理算法解決了複製算法隨着對象存活率升高,時間複雜度變高的缺點。

調用後的過程如下:

1.執行標記-清除算法

2.將存活對象往一側移動,並且清理邊界外的內存區域


優點: 不會因爲存活率變高,而效率降低

缺點: 時間複雜度較高



Generational Collection(分代收集算法)

  • 目前幾乎所有商業虛擬機均採用了分代收集算法

  • 在(JDK1.8)JVM中,內存分成三個區域:新生代,老年代,元數據區(原是永久代)

  • 分代收集算法是基於複製算法和標記-整理算法的特點改良後的算法,專門針對新生代與老年代的特點進行設計。


新生代

作用: 存放新生的對象

特點: 創建對象頻繁,但存活率低。老年代可作爲空間不足時的備用內存使用。

內存結構簡易圖:

在這裏插入圖片描述

  • 分成三個區域: Eden、ServivorFrom、ServivorTo

    Eden區:每次創建對象分配空間的區域(但是如果對象所佔空間太大,也可能會在老年區分配)

    ServivorFrom區:存放上次GC回收後,存活的對象

    ServivorTo區:存放當前GC回收後,存活的對象(進入該區域的對象年齡會+1)

GC回收算法: 複製算法

  • GC方式:MinorGC

  • 1.每次創建對象會在Eden中分配空間,若Eden空間不足,則調用MinorGC進行回收。

    2.將Eden區和ServivorFrom區進行標記,接着將存活對象複製到ServivorTo區上

    3.存活的對象滿足年齡條件或者ServivorTo空間不足時,會將對象複製到老年代區域

    4.清除Eden區和ServivorFrom區

    5.交換ServivorFrom區域和ServivorTo區域,…執行1…


老年代

作用: 存放生命週期長的對象

特點: 存活率高,沒有備用空間

內存結構簡易圖:

在這裏插入圖片描述

GC回收算法: 標記-整理

  • GC方式:MajorGC
  • 僅在調用MinorGC後,有對象從新生代進入老年代時,纔會調用MajorGC
  • 該區域的GC回收,通常會使用標記-整理,而不是標記-清理(減少碎片化)
  • 該區域若空間不足以分給,會拋出OutOfMemory錯誤
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章