文章目錄
基本概念
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錯誤