如何獲取小程序源碼天津seo推廣
上一篇:05-JVM內(nèi)存分配機(jī)制深度剖析
堆中幾乎放著所有的對象實(shí)例,對堆垃圾回收前的第一步就是要判斷哪些對象已經(jīng)死亡(即不能再被任何途徑使用的對象)。
1.引用計(jì)數(shù)法
給對象中添加一個引用計(jì)數(shù)器,每當(dāng)有一個地方引用它,計(jì)數(shù)器就加1;當(dāng)引用失效,計(jì)數(shù)器就減1;任何時候計(jì)數(shù)器為0的對象就是不可能再被使用的。
這個方法實(shí)現(xiàn)簡單,效率高,但是目前主流的虛擬機(jī)中并沒有選擇這個算法來管理內(nèi)存,其最主要的原因是它很難解決對象之間相互循環(huán)引用的問題。 所謂對象之間的相互引用問題,如下面代碼所示:除了對象objA 和 objB 相互引用著對方之外,這兩個對象之間再無任何引用。但是他們因?yàn)榛ハ嘁脤Ψ?#xff0c;導(dǎo)致它們的引用計(jì)數(shù)器都不為0,于是引用計(jì)數(shù)算法無法通知 GC 回收器回收他們。
public class ReferenceCountingGc {Object instance = null;public static void main(String[] args) {ReferenceCountingGc objA = new ReferenceCountingGc();ReferenceCountingGc objB = new ReferenceCountingGc();objA.instance = objB;objB.instance = objA;objA = null;objB = null;}
}
2.可達(dá)性分析算法
將“GC Roots” 對象作為起點(diǎn),從這些節(jié)點(diǎn)開始向下搜索引用的對象,找到的對象都標(biāo)記為非垃圾對象,其余未標(biāo)記的對象都是垃圾對象
GC Roots根節(jié)點(diǎn):線程棧的本地變量、靜態(tài)變量、本地方法棧的變量等等
3.常見引用類型
java的引用類型一般分為四種:強(qiáng)引用、軟引用、弱引用、虛引用
**強(qiáng)引用:**普通的變量引用
public static User user = new User();
**軟引用:**將對象用SoftReference軟引用類型的對象包裹,正常情況不會被回收,但是GC做完后發(fā)現(xiàn)釋放不出空間存放新的對象,則會把這些軟引用的對象回收掉。軟引用可用來實(shí)現(xiàn)內(nèi)存敏感的高速緩存。
public static SoftReference<User> user = new SoftReference<User>(new User());
軟引用在實(shí)際中有重要的應(yīng)用,例如瀏覽器的后退按鈕。按后退時,這個后退時顯示的網(wǎng)頁內(nèi)容是重新進(jìn)行請求還是從緩存中取出呢?這就要看具體的實(shí)現(xiàn)策略了。
- 如果一個網(wǎng)頁在瀏覽結(jié)束時就進(jìn)行內(nèi)容的回收,則按后退查看前面瀏覽過的頁面時,需要重新構(gòu)建
- 如果將瀏覽過的網(wǎng)頁存儲到內(nèi)存中會造成內(nèi)存的大量浪費(fèi),甚至?xí)斐蓛?nèi)存溢出
弱引用: 將對象用WeakReference軟引用類型的對象包裹,弱引用跟沒引用差不多,GC會直接回收掉,很少用
public static WeakReference<User> user = new WeakReference<User>(new User());
虛引用: 虛引用也稱為幽靈引用或者幻影引用,它是最弱的一種引用關(guān)系,幾乎不用
4.finalize()方法最終判定對象是否存活
即使在可達(dá)性分析算法中不可達(dá)的對象,也并非是“非死不可”的,這時候它們暫時處于“緩刑”階段,要真正宣告一個對象死亡,至少要經(jīng)歷再次標(biāo)記過程。
標(biāo)記的前提是對象在進(jìn)行可達(dá)性分析后發(fā)現(xiàn)沒有與GC Roots相連接的引用鏈。
- 第一次標(biāo)記并進(jìn)行一次篩選。
篩選的條件是此對象是否有必要執(zhí)行finalize()方法。
當(dāng)對象沒有覆蓋finalize方法,對象將直接被回收。 - 第二次標(biāo)記
如果這個對象覆蓋了finalize方法,finalize方法是對象脫逃死亡命運(yùn)的最后一次機(jī)會,如果對象要在finalize()中成功拯救自己,只要重新與引用鏈上的任何的一個對象建立關(guān)聯(lián)即可,譬如把自己賦值給某個類變量或?qū)ο蟮某蓡T變量,那在第二次標(biāo)記時它將移除出“即將回收”的集合。如果對象這時候還沒逃脫,那基本上它就真的被回收了。
注意:一個對象的finalize()方法只會被執(zhí)行一次,也就是說通過調(diào)用finalize方法自我救命的機(jī)會就一次。
示例代碼:
public class OOMTest {public static void main(String[] args) {List<Object> list = new ArrayList<>();int i = 0;int j = 0;while (true) {list.add(new User(i++, UUID.randomUUID().toString()));new User(j--, UUID.randomUUID().toString());}}
}//User類需要重寫finalize方法
@Override
protected void finalize() throws Throwable {OOMTest.list.add(this);System.out.println("關(guān)閉資源,userid=" + id + "即將被回收");
}
finalize()方法的運(yùn)行代價(jià)高昂, 不確定性大, 無法保證各個對象的調(diào)用順序, 如今已被官方明確聲明為不推薦使用的語法。 有些資料描述它適合做“關(guān)閉外部資源”之類的清理性工作, 這完全是對finalize()方法用途的一種自我安慰。 finalize()能做的所有工作, 使用try-finally或者其他方式都可以做得更好、更及時, 所以建議大家完全可以忘掉Java語言里面的這個方法。
5.如何判斷一個類是無用的類
方法區(qū)主要回收的是無用的類,那么如何判斷一個類是無用的類呢?
類需要同時滿足下面3個條件才能算是 “無用的類” :
- 該類所有的對象實(shí)例都已經(jīng)被回收,也就是 Java 堆中不存在該類的任何實(shí)例。
- 加載該類的 ClassLoader 已經(jīng)被回收。
- 該類對應(yīng)的 java.lang.Class 對象沒有在任何地方被引用,無法在任何地方通過反射訪問該類的方法。