Java垃圾回收機制
垃圾回收中有兩種經典的方法:
- 引用計數法,
Python
中使用的此種演算法,但是解決不了迴圈引用的問題. - 可達性演算法,主流
java
虛擬機器採用此種方法,從GC ROOTS
作為起點,不能到達的物件則被判定垃圾
每一個執行緒都有自己獨享的區域,程式計數器,虛擬機器棧,本地方法棧的生命週期都與執行緒一致.因而需要回收的有虛擬機器堆,靜態區以及常量池.
常用的GC演算法
mark-sweep
標記清除法:
將待清理的垃圾標記後直接清空,會產生許多記憶體碎片.
mark-copy
標記複製法:
將記憶體對半分,總是空一塊區域.將其中一側的存活物件複製到另一側,然後將這一側全部清空,記憶體浪費比較嚴重.
mark-compact
標記整理法:
將物件清理後,對存活物件進行整理挪動,避免了上面兩種方法的缺點,但是效率不高.
generation-collect
分代收集演算法:
HotSpot
(JDK7
)中的記憶體分佈思想:
將記憶體分成了三塊:年輕代,老年代,永久代.其中年輕代又細分為eden
,S1
和S1
(survivor
)三個區.
GC
的主要過程包括Young GC
(minor GC
), 它綜合運用了mark-sweep eden
和mark-copy eden->s0/s1
兩種方法,以及Full GC
(major GC
).
Young GC
:
- 初始時物件會被分配在
eden
區:
- 隨著時間推移,
eden
- 當
eden
區域滿了時,觸發Young GC
:
標識出不可達物件,並將可達物件移至s0
.從而清理了eden
區域. - 當
eden
區域又滿了時,再次觸發Young GC
,此時s0
也有了垃圾物件:
將s0
與eden
區域的不可達物件標識出,並將可達物件移至s1
.
如此循往,有的存活物件在s0
與s1
中不斷移動,因此產生了"代齡".物件在年輕代的三個區中移動,每移動一次,代齡+1
.在到達一定閾值(8
)後,將晉升至老年代.
還有一種情況是,當物件較大,eden
區放不下,將會直接分配到老年代區.
Full GC
當老年代的區域也放滿了,將會觸發Full GC
,耗時比較長,此時Java
將會暫停所有其他執行緒,會出現應用卡頓的現象,即stop the world
經典的垃圾回收器
圖中除G1
以外,Serial
,ParNew
以及Parellel Scavenge
都是回收年輕代的垃圾收集器;Serial Old
,Parallel Old
以及CMS
是用來回收老年代的垃圾.除了CMS
會部分採用stop th world
方式,其餘的都是採用stop the world
方式.圖中的連線代表幾種典型的組合應用.
Serial
: 單執行緒採用mark-copy
演算法ParNew
: 多執行緒版本Serial
Parallel Scavenge
: 相比ParNew
,提供了-XX:MaxGCPauseMillis
(最大垃圾回收停頓時間);-XX:GCTimeRatio
(垃圾回收時間與總時間佔比),可以控制吞吐量Serial Old
:Serial
的老年代版本,單執行緒採用mark-compact
,可以作為CMS
失敗時的後備選擇Parallel Old
:Parallel Scavenge
的老年代版本,使用多執行緒以及mark-compact
,只能和Parallel Scavenge
配合使用CMS
:Concurrent Mark Sweep
,是多併發的標記清理,主要包括四個階段:initial mark
: 標記GC Root
的僅下一級物件,會出現短暫的STW
concurrent mark
: 多執行緒向下繼續標識所有關聯物件,不會出現STW
remark
: 重新標記一遍步驟2執行時,系統新產的垃圾物件,會出現短暫的STW
concurrent sweep
: 多執行緒進行標記清理演算法.在清理的過程中仍然可能會有新垃圾物件產生,此時只能等到下一次的GC
了.
CMS
將較長的STW
分隔成為兩個較短的STW
,可以大大改善GC
系統卡頓的情況.但是仍然存在以下缺點:
- 由於併發進行,
CMS
在執行回收時會增加對堆記憶體的佔用,即CMS
需要在老年代堆記憶體用盡之前完成垃圾回收,否則回收失敗,將會觸發擔保機制,使用serial old
以STW
的方式進行一次GC
,造成較大停頓. - 無法整理空間碎片,老年代空間會隨著時間推移被逐漸耗盡.
- 對
cpu
資源敏感
G1
垃圾回收器:它被設計的初衷是用於替代CMS
,與CMS
不同,它採用的壓縮演算法,並且採用region
的分割槽方式,從而簡化了垃圾收集器,並且減少了記憶體中產生的碎片,也允許使用者指定期望的垃圾回收停頓.
堆分為一系列相同大小的
region
,在虛擬記憶體中是連續的.每一塊region
都有自己的角色,例如eden
,survivor
或者old
,這樣可以方便隨意調整這些角色的區域大小.
G1
的young gc
採用多執行緒的方式,進行清除或者複製,會帶來STW
.但是會計算下一次youn gc
的eden
區域以及survivor
的大小,也會考慮使用者設定的停頓時長.
G1
的old gc
可以分解為以下步驟:
- 初始標記,會帶來
STW
- 從
GC ROOT
開始掃描可達區域 - 併發標記
- 再次標記,會帶來
STW
- 併發清除,帶來
STW
,存活物件較少的region
會被優先清除.Young
區和Old
區的物件有可能會被同時清理,因此也可稱為mixed GC
.
References:
詳解 JVM Garbage First(G1) 垃圾收集器
JVM 垃圾收集器Serial +Serial Old+ParNew+Parallel Scavenge+Parallel Old+CMS+G1