Java GC垃圾回收算法分析
對象探活
在討論回收算法前,更為重要的問題是如何判斷一個對象是否可以被回收?
引用計數(shù)算法
每個對象會維護一個count,當(dāng)有一個對象的屬性引用自己時,count自增。當(dāng)為0時,意味可被回收。
缺點:
- 需要頻繁執(zhí)行維護count的操作
- 無法解決循環(huán)依賴問題。比如下圖中的D與E節(jié)點,已經(jīng)沒有其他對象的內(nèi)部在引用時,應(yīng)該被判定為可回收對象。但是由于雙方相互引用,形成了循環(huán)依賴,無法被回收。
可達性分析(目前主流虛擬機垃圾回收器采取的算法):
將符合的GC Roots作為初始的存活對集合,以該集合中的Roots為起點,探索所有能夠被Roots引用到的對象,并加入到Roots集合中,這個過程稱之為標記。未被探索到的對象即是可回收對象(死亡的)。
優(yōu)點:可以解決引用計數(shù)算法的循環(huán)依賴問題。從GC Roots出發(fā),無法探測到循環(huán)依賴的對象,那么就會進行回收。
那么什么樣的對象可以被作為Root對象(包括但不限于)
- 局部變量表中的對象引用
- 已被加載的靜態(tài)屬性引用的對象
- 常量對象引用
強-軟-弱-虛引用
有些時候,我們有這樣一種需求,當(dāng)內(nèi)存足夠時,會保留一些對象,方便后續(xù)調(diào)用。當(dāng)內(nèi)存不足時,將這些對象回收,留出更多的內(nèi)存空間。系統(tǒng)的很多緩存功能符合上述條件。
// 強引用:只要引用可達,就永遠不會被回收 Object obj = new Object(); // 軟引用:堆內(nèi)存不夠時被回收 SoftReference<Object> softReference = new SoftReference<>(obj); // 弱引用:只要觸發(fā)GC就會被回收 WeakReference<Object> weakReference = new WeakReference<>(obj); /** * 虛引用:虛引用不會決定對象的生命周期,如果一個對象持有虛引用,那么和沒有引用一樣,get永遠返回null。 * * 需要配合引用隊列使用,當(dāng)垃圾回收器準備回收一個虛引用時,會將其加入到引用隊列中。 * * 程序可以根據(jù)虛引用是否入隊,來了解對象是否即將被垃圾回收,進而執(zhí)行一些響應(yīng)操作。 */ Object obj2 = new Object(); ReferenceQueue<Object> referenceQueue = new ReferenceQueue<>(); PhantomReference<Object> phantomReference = new PhantomReference<>(obj2, referenceQueue); System.out.println(phantomReference.isEnqueued()); // false obj2 = null; System.gc(); Thread.sleep(500); System.out.println(phantomReference.isEnqueued()); // true
標記清除
- 根據(jù)可達性分析,標記可以回收的對象
- 將被標記的可回收對象進行回收
內(nèi)存碎片問題:造成了不連續(xù)的內(nèi)存碎片。當(dāng)有大對象需要存儲時,若連續(xù)的碎片空間存儲不下, 難免會再次觸發(fā)垃圾回收的操作。
標記復(fù)制
- 保留一半的空間,每次只使用一半的空間。
- 每次GC,根據(jù)可達性分析,將存活對象復(fù)制到另一片空間。
- 釋放本次使用一半空間。
優(yōu)點:
- 只需針對一半的空間進行回收。
- 避免了內(nèi)存碎片問題。
- 只需要按順序分配內(nèi)存空間即可,實現(xiàn)簡單。
缺點:
- 可使用的內(nèi)存空間縮減了一半。
- 執(zhí)行時需要STW,導(dǎo)致掛起執(zhí)行的用戶線程
標記整理
標記清除的改進
- 根據(jù)可達性分析,對可以回收的對象進行標記
- 將未被標記的存活對象移動到另一端,然后直接清理掉端邊界以外的內(nèi)存
優(yōu)點:解決了標記清除的內(nèi)存碎片問題
缺點:
- 相比標記清除的直接釋放,需要更多的時間來完成整理部分的操作
- 執(zhí)行時需要STW,導(dǎo)致掛起執(zhí)行的用戶線程
回收算法的在堆內(nèi)存上的應(yīng)用
新生代
根據(jù)新生代的特點,對象存活率較低,應(yīng)用標記復(fù)制算法。分配內(nèi)存空間時,使用Eden區(qū)與一塊Survivor區(qū),GC后將存活的對象放入到另一塊Survivor區(qū)。如果另一塊Survivor區(qū)不夠存放存活對象,多數(shù)情況下會使用老年代進行分配擔(dān)保(分配擔(dān)保:將無法存儲的存活對象放入其他存儲空間)
循環(huán):
Eden + S0 -> S1 (將Eden 與 S0存活的對象復(fù)制到S1)
Eden + S1 -> S0
Eden + S0 -> S1
老年代
根據(jù)老年代的特點,對象存活率較高,一般用標記-清除,標記-整理算法。
到此這篇關(guān)于Java GC垃圾回收算法分析的文章就介紹到這了,更多相關(guān)Java GC垃圾回收內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java開發(fā)環(huán)境配置教程(win7 64bit)
這篇文章主要為大家詳細介紹了win7 64bit下Java開發(fā)環(huán)境的配置教程,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-08-08淺談Java中OutOfMemoryError問題產(chǎn)生原因
本文主要介紹了淺談Java中OutOfMemoryError問題產(chǎn)生原因,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-06-06springboot應(yīng)用訪問zookeeper的流程
這篇文章主要介紹了springboot應(yīng)用訪問zookeeper的流程,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-01-01