Java怎樣判斷堆區(qū)中的對象可以被回收了
如何判斷堆區(qū)中的對象可以被回收了
在Java中,垃圾回收機(jī)制會幫助我們自動回收不再被使用的對象,已到達(dá)即使釋放內(nèi)存的效果,但是Java又是怎么知道哪些對象不會再被我們繼續(xù)使用了呢,希望你通過本篇文章,理解引用計(jì)數(shù)法與可達(dá)性分析法的運(yùn)行方式
垃圾回收機(jī)制
在C/C++中,一個(gè)對象如果不再使用,就要手動將其釋放掉,但是很多程序員在編寫程序的時(shí)候經(jīng)常忘記將一些對象回收,從而就導(dǎo)致了內(nèi)存泄漏
在Java中為了簡化對象的內(nèi)存釋放,引入了自動的垃圾回收機(jī)制,通過垃圾回收器把不再使用的對象完成自動回收,垃圾回收器主要負(fù)責(zé)堆上的內(nèi)存回收,那么垃圾回收器又是如何知道哪些對象可以被回收了呢?
在Java中,一個(gè)對象是否可以被回收,主要是看這個(gè)對象是否被引用,如果對象被引用了,說明對象還在使用,是不可以被回收的,比如說如下代碼中,堆內(nèi)存中的Demo對象被demo引用,那么堆中的Demo對象就不會被回收:
public class Demo { public static void main(String[] args) { Demo demo = new Demo(); } }
若此時(shí)將demo的引用設(shè)置為null:
demo = null;
那么此時(shí)Demo對象就處于了沒有被引用的狀態(tài):
此時(shí)Demo會被垃圾回收器進(jìn)行回收,那么垃圾回收器又是怎么知道Demo對象目前沒有被引用呢???
引用計(jì)數(shù)法
引用計(jì)數(shù)法會為每一個(gè)對象維護(hù)一個(gè)引用計(jì)數(shù)器,當(dāng)對象被引用時(shí)加一,取消引用時(shí)減一,在上面的代碼中,demo引用了堆上的Demo對象,所以Demo對象的引用計(jì)數(shù)器就加一,當(dāng)把demo賦值為null的時(shí)候,也就是取消了Demo的引用,此時(shí)Demo對象引用計(jì)數(shù)器將減一成為0,此時(shí)垃圾回收器就認(rèn)為Demo對象此時(shí)沒有被任何引用,可以回收
但是此時(shí)會出現(xiàn)一個(gè)問題,如果我new了兩個(gè)對象A與B,并且A對象與B對象的互為彼此的成員變量,那么就會出現(xiàn)循環(huán)引用的現(xiàn)象,此時(shí)A對象被棧內(nèi)存中的a1引用,也被B對象中的a變量引用,那么他的引用技術(shù)器應(yīng)該為2:
public class Demo { public static void main(String[] args) { A a1 = new A(); B b1 = new B(); a1.b = b1; b1.a = a1; } } class A { B b; } class B { A a; }
那么此時(shí),我取消a1、b1對A與B的引用
a1 = null; b1 = null;
我們已經(jīng)無法在程序中獲取到A與B對象了,因?yàn)樗麄儍蓚€(gè)除了彼此間的引用關(guān)系,已經(jīng)沒有任何引用能夠找到他們,所以按照常理來說,A與B對象都不會在程序中再使用了,理應(yīng)被垃圾回收器進(jìn)行回收,但是又由于存在彼此間的引用關(guān)系,引用計(jì)數(shù)器的值并不是0,那么此時(shí)垃圾回收器又會認(rèn)為A與B都存在被引用的關(guān)系,所以并不會回收這兩個(gè)類
那么這種情況顯然是不對的,無法回收的對象有可能會導(dǎo)致內(nèi)存泄漏,所以Java并沒有使用這種方法來判斷類是否應(yīng)該被回收,而是使用了另外一種方式
可達(dá)性分析法
Java使用的是可達(dá)性分析算法來判斷對象是否可以被回收
可達(dá)性分析法將對象分為兩類:
- 垃圾回收根對象(GC Root)
- 普通對象
對象與對象之間存在引用關(guān)系,形成一個(gè)引用鏈, 可達(dá)性分析算法就是指GC Root對象到某個(gè)對象間是可達(dá)的,即從GC Root對象開始,通過引用對象可以找到的對象愛國,即認(rèn)為該對象還不能被回收
此時(shí)B、C、D對象都可以通過引用被GC Root對象找到,即他們都是可達(dá)的,所以不會被視為可回收的對象,但是如果對象A與對象B之間取消引用關(guān)系,那么即使對象C與對象D任然存在引用關(guān)系,但他們是不可達(dá)的,因此他們會被回收
Java虛擬機(jī)會持有一個(gè)所有GC Root對象的列表,用來判斷哪些對象是不可達(dá)的,不可達(dá)的對象將被垃圾回收器進(jìn)行回收:
再次回看上面的案例,如果使用可達(dá)性分析法,那么堆內(nèi)存中應(yīng)該存在一個(gè)GC Root對象引用了主線程里面mian方法的棧幀中的對象,此時(shí)如果a1與b1不再引用堆中的對象,那么就算A對象與B對象存在引用關(guān)系,但是他們是不可達(dá)的,就會被視為等待回收的對象:
那么哪些對象可以被當(dāng)中GC Root對象呢?
主要有四種GC Root對象:
- 1.線程Thread對象
- 引用線程棧幀中的方法、參數(shù)、局部變量等等,上面我們的案例中就是線程Thread對象引用了mian方法棧幀中的a1與b1
- 2.類加載器加載到的java.lang.Class對象
- 引用類中的靜態(tài)變量
- 3.監(jiān)視器對象
- 用來保存同步鎖synchronized關(guān)鍵字持有的對象
- 4.本地方法方法調(diào)用時(shí)使用的全局對象
- 由Java虛擬機(jī)來控制調(diào)用
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
Spring Cloud體系實(shí)現(xiàn)標(biāo)簽路由的方法示例
這篇文章主要介紹了Spring Cloud體系實(shí)現(xiàn)標(biāo)簽路由的方法示例,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2019-05-05mybatis框架xml下trim中的prefix與suffix等標(biāo)簽的用法
這篇文章主要介紹了mybatis框架xml下trim中的prefix與suffix等標(biāo)簽的用法,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-07-07SpringBoot項(xiàng)目啟動時(shí)初始化操作常見的幾種方式
這篇文章主要介紹了SpringBoot項(xiàng)目啟動時(shí)初始化操作常見的幾種方式,這些方法可以幫助開發(fā)者在服務(wù)啟動時(shí)自動執(zhí)行必要的初始化操作,需要的朋友可以參考下2025-02-02Springboot?通過FastJson實(shí)現(xiàn)bean對象和Json字符串互轉(zhuǎn)問題
這篇文章主要介紹了Springboot?通過FastJson實(shí)現(xiàn)bean對象和Json字符串互轉(zhuǎn),本文嘗試驗(yàn)證兩種場景給大家詳細(xì)介紹,對Springboot?FastJson實(shí)現(xiàn)bean和Json互轉(zhuǎn)問題,感興趣的朋友一起看看吧2022-08-08java注解處理器學(xué)習(xí)在編譯期修改語法樹教程
這篇文章主要為大家介紹了java注解處理器學(xué)習(xí)在編譯期修改語法樹教程,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-09-09基于Java堆內(nèi)存的10個(gè)要點(diǎn)的總結(jié)分析
本篇文章是對Java堆內(nèi)存的10個(gè)要點(diǎn)進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下2013-05-05springboot的maven多模塊混淆jar包的實(shí)現(xiàn)方法
springboot可以使用proguard-maven-plugin 這個(gè)插件 在 pom.xml 中自定義proguard 的指令,本文基于 springboot + maven + proguard 的maven多模塊架構(gòu)進(jìn)行代碼混淆,需要的朋友可以參考下2024-03-03