摘要:當(dāng)一個(gè)對(duì)象被一個(gè)或一個(gè)以上的引用變量所引用時(shí),它處于可達(dá)狀態(tài),不可能被系統(tǒng)垃圾回收機(jī)制回收。虛引用主要用于跟蹤對(duì)象被垃圾回收的狀態(tài),虛引用不能多帶帶使用,虛引用必須和引用隊(duì)列聯(lián)合使用。
當(dāng)程序創(chuàng)建對(duì)象、數(shù)組等引用類型實(shí)體時(shí),系統(tǒng)都會(huì)在堆內(nèi)存中為之分配一塊內(nèi)存區(qū),對(duì)象就保存在這塊內(nèi)存區(qū)中,當(dāng)這塊內(nèi)存不再被任何引用變量引用時(shí),這塊內(nèi)存就變成垃圾,等待垃圾回收機(jī)制進(jìn)行回收。垃圾回收機(jī)制具有如下特征。
垃圾回收機(jī)制只負(fù)責(zé)回收內(nèi)存中的對(duì)象,不會(huì)回收任何物理資源(例如數(shù)據(jù)庫連接、網(wǎng)絡(luò)IO等資源)
程序無法精確控制垃圾回收的運(yùn)行,垃圾回收會(huì)在合適的時(shí)候進(jìn)行。
在垃圾回收機(jī)制回收任何對(duì)象之前,總會(huì)先調(diào)用它的finalize()方法,該方法可能使該對(duì)象重新復(fù)活(讓一個(gè)引用變量重新引用該對(duì)象),從而導(dǎo)致垃圾回收機(jī)制取消回收。
對(duì)象在內(nèi)存中的狀態(tài)當(dāng)一個(gè)對(duì)象在堆內(nèi)存中運(yùn)行時(shí),根據(jù)它被引用變量所引用的狀態(tài),可以把它所處的狀態(tài)分成如下三種:
可達(dá)狀態(tài):當(dāng)一個(gè)對(duì)象被創(chuàng)建后,若有一個(gè)以上的引用變量引用它,則找個(gè)對(duì)象在程序中處于可達(dá)狀態(tài),程序可通過引用變量來調(diào)用該對(duì)象的實(shí)例變量和方法。
可恢復(fù)狀態(tài):如果程序中某個(gè)對(duì)象不再有任何引用變量引用它,它就進(jìn)入了可恢復(fù)狀態(tài)。在這種狀態(tài)下,系統(tǒng)的垃圾回收機(jī)制準(zhǔn)備回收該對(duì)象所占用的內(nèi)存,在回收該對(duì)象之前,系統(tǒng)會(huì)調(diào)用所有可恢復(fù)狀態(tài)對(duì)象的finalize()方法進(jìn)行資源清理。如果系統(tǒng)在調(diào)用finalize()方法時(shí)重新讓一個(gè)引用變量引用該對(duì)象,則這個(gè)對(duì)象會(huì)再次變成可達(dá)狀態(tài);否則該對(duì)象將進(jìn)入不可達(dá)狀態(tài)。
不可達(dá)狀態(tài):當(dāng)對(duì)象與所有引用變量的關(guān)聯(lián)都被切斷,且系統(tǒng)已經(jīng)調(diào)用所有對(duì)象的finalize()方法后依然沒有使該對(duì)象變成可達(dá)狀態(tài),那么這個(gè)對(duì)象將永久性地失去引用,最后變成不可達(dá)狀態(tài)。只有當(dāng)一個(gè)對(duì)象處于不可達(dá)狀態(tài)時(shí),系統(tǒng)才會(huì)真正回收該對(duì)象所占有的資源。
public class StatusTranfer { public static void test() { String a = new String("知乎、掘金、SegmentFault"); a = new String("Java"); } public static void main(String[] args) { test(); } }
當(dāng)程序執(zhí)行test方法的第一行代碼時(shí),代碼定義了一個(gè)a變量,并讓該變量指向"知乎、掘金、SegmentFault"字符串,該代碼執(zhí)行結(jié)束后"知乎、掘金、SegmentFault"字符串對(duì)象處于可達(dá)狀態(tài)。
當(dāng)程序執(zhí)行test方法的第二行代碼時(shí),代碼再次創(chuàng)建了"Java"字符串對(duì)象,并讓a變量指向該對(duì)象。此時(shí)"知乎、掘金、SegmentFault"字符串對(duì)象處于可恢復(fù)狀態(tài),而"Java"字符串處于可達(dá)狀態(tài)。
一個(gè)對(duì)象可以被一個(gè)方法的局部變量引用,也可以被其他類的類變量引用,或被其他對(duì)象的實(shí)例變量引用。當(dāng)某個(gè)對(duì)象被其他類的類變量引用時(shí),只有該類被銷毀后,該對(duì)象才會(huì)進(jìn)入可恢復(fù)狀態(tài);當(dāng)某個(gè)對(duì)象被其他對(duì)象的實(shí)例變量引用時(shí),只有當(dāng)該對(duì)象被銷毀后,該對(duì)象才會(huì)進(jìn)入可恢復(fù)狀態(tài)。
強(qiáng)制垃圾回收當(dāng)一個(gè)對(duì)象失去引用后,系統(tǒng)何時(shí)調(diào)用它的finalize()方法對(duì)它進(jìn)行資源清理,何時(shí)它會(huì)變成不可達(dá)狀態(tài),系統(tǒng)何時(shí)回收它所占有的內(nèi)存,對(duì)于程序完全透明。程序只能控制一個(gè)對(duì)象何時(shí)不再被任何引用變量引用,絕不能控制它何時(shí)被回收。
程序強(qiáng)制系統(tǒng)垃圾回收與如下兩種方式
調(diào)用System類的gc()靜態(tài)方法:System.gc()
調(diào)用Runtime對(duì)象的gc()實(shí)例方法:Runtime.getRuntime().gc()
finalize方法在垃圾回收機(jī)制回收某個(gè)對(duì)象所占用的內(nèi)存之前,通常要求程序調(diào)用適當(dāng)?shù)姆椒▉砬謇碣Y源,在設(shè)有明確清理資源的情況下,Java提供了默認(rèn)機(jī)制來清理該對(duì)象的資源,這個(gè)機(jī)制就是finalize()方法。該方法是定義在Object類里的實(shí)例方法
protected void finalize() throws Throwable
finalize()方法具有如下4個(gè)特點(diǎn):
永遠(yuǎn)不要主動(dòng)調(diào)用某個(gè)對(duì)象的finalize()方法,該方法應(yīng)交給垃圾回收機(jī)制調(diào)用。
finalize()方法何時(shí)被調(diào)用,是否被調(diào)用具有不確定性,不要把finalize()方法當(dāng)成一定會(huì)被執(zhí)行的方法。
當(dāng)JVM執(zhí)行可恢復(fù)對(duì)象的fianlize()方法時(shí),可能使該對(duì)象或系統(tǒng)中其他對(duì)象重新編程可達(dá)狀態(tài)。
當(dāng)JVM執(zhí)行finalize()方法時(shí)出現(xiàn)異常時(shí),垃圾回收機(jī)制不會(huì)報(bào)告異常,程序繼續(xù)執(zhí)行。
public class FinalizeTest { private static FinalizeTest ft = null; public void info() { System.out.println("測試資源清理的finalize方法"); } public static void main(String[] args) { //創(chuàng)建FinalizeTest對(duì)象立即進(jìn)入可恢復(fù)狀態(tài) new FinalizeTest(); //通知系統(tǒng)進(jìn)行資源回收 System.gc(); //強(qiáng)制垃圾回收機(jī)制調(diào)用可恢復(fù)對(duì)象的finalize()方法 Runtime.getRuntime().runFinalization(); System.runFinalization(); ft.info(); } public void finalize() { //讓tf引用到試圖回收的可恢復(fù)對(duì)象,即可恢復(fù)對(duì)象重新變成可達(dá) ft = this; } }對(duì)象的軟、弱和虛引用 強(qiáng)引用(StrongReference)
Java程序中最常見的引用方式。程序創(chuàng)建一個(gè)對(duì)象,并把這個(gè)對(duì)象賦給一個(gè)引用變量,程序通過該引用變量來操作實(shí)際的對(duì)象。當(dāng)一個(gè)對(duì)象被一個(gè)或一個(gè)以上的引用變量所引用時(shí),它處于可達(dá)狀態(tài),不可能被系統(tǒng)垃圾回收機(jī)制回收。
軟引用(SoftReference)通過SoftReference類來實(shí)現(xiàn),當(dāng)一個(gè)對(duì)象只有軟引用時(shí),它有可能被垃圾回收機(jī)制回收。當(dāng)系統(tǒng)內(nèi)存空間足夠時(shí),它不會(huì)被系統(tǒng)回收,程序也可使用該對(duì)象;當(dāng)系統(tǒng)內(nèi)存空間不足時(shí),系統(tǒng)可能會(huì)回收它。軟引用通常用于對(duì)內(nèi)存敏感的程序中。
弱引用(WeakReference)通過WeakReference類實(shí)現(xiàn),弱引用和軟引用很像,但弱引用的引用級(jí)別更低。對(duì)于只有弱引用的對(duì)象而已,當(dāng)系統(tǒng)垃圾回收機(jī)制運(yùn)行時(shí),不管系統(tǒng)內(nèi)存是否足夠,總會(huì)回收該對(duì)象所占用的內(nèi)存。當(dāng)然,并不是說當(dāng)一個(gè)對(duì)象只有弱引用時(shí),它就會(huì)立即被回收——正如那些失去引用的對(duì)象一樣,必須等到系統(tǒng)垃圾回收機(jī)制運(yùn)行時(shí)才會(huì)被回收。
虛引用(PhantomReference)通過PhantomReference類實(shí)現(xiàn),虛引用完全類似于沒有引用。虛引用對(duì)對(duì)象本身沒有太大影響,對(duì)象甚至感覺不到虛引用的存在。如果一個(gè)對(duì)象只有一個(gè)虛引用時(shí),那么它和沒有引用的效果大致相同。虛引用主要用于跟蹤對(duì)象被垃圾回收的狀態(tài),虛引用不能多帶帶使用,虛引用必須和引用隊(duì)列(ReferenceQueue)聯(lián)合使用。程序可以通過檢查與虛引用關(guān)聯(lián)的引用隊(duì)列中是否已經(jīng)包含了該虛引用,從而了解虛引用所引用的對(duì)象被系統(tǒng)垃圾回收過程。
上面三個(gè)引用類都包含了一個(gè)get()方法,用于獲取被它們所引用的對(duì)象。
引用隊(duì)列由java.lang.ref.ReferenceQueue類表示,它用于保存被回收后對(duì)象的引用。當(dāng)聯(lián)合使用軟引用、弱引用和引用隊(duì)列時(shí),系統(tǒng)在回收被引用的對(duì)象之后,將把被回收對(duì)象的引用添加到關(guān)聯(lián)的引用隊(duì)列中。與軟引用和弱引用不同的是,虛引用在對(duì)象被釋放之前,將把它對(duì)應(yīng)的虛引用添加到它關(guān)聯(lián)的引用隊(duì)列中,這使得可以在對(duì)象被回收之前采取行動(dòng)。
public class ReferenceTest { public static void main(String[] args) throws Exception { //創(chuàng)建一個(gè)字符串對(duì)象 String str = new String("克利夫蘭騎士"); //創(chuàng)建一個(gè)弱引用,讓此弱引用引用到到"克利夫蘭騎士"字符串 WeakReference wr = new WeakReference(str); //① //切斷str引用和"克利夫蘭騎士"字符串之間的引用 str = null; //② //取出弱引用所引用的對(duì)象 System.out.println(wr.get()); //③ //強(qiáng)制垃圾回收 System.gc(); System.runFinalization(); //再次取出弱引用所引用的對(duì)象 System.out.println(wr.get()); //④ } }
當(dāng)程序執(zhí)行①行代碼時(shí),系統(tǒng)創(chuàng)建了一個(gè)弱引用對(duì)象,并讓該對(duì)象和str引用同一個(gè)對(duì)象。
當(dāng)程序執(zhí)行②行代碼時(shí),程序切斷了str和"克利夫蘭騎士"字符串對(duì)象之間的引用關(guān)系。
當(dāng)程序執(zhí)行③行代碼時(shí),由于本程序不會(huì)導(dǎo)致內(nèi)存緊張,此時(shí)程序通常還不會(huì)回收弱引用wr所引用的對(duì)象,因此在③號(hào)代碼處可以看到輸出字符串。
之后,調(diào)用System.gc();和System.runFinalization();通知系統(tǒng)進(jìn)行垃圾回收,如果系統(tǒng)立即進(jìn)行垃圾回收,那么就會(huì)將弱引用wr所引用的對(duì)象回收。在④號(hào)代碼處將看到輸出null。
采用String str = "克利夫蘭騎士";代碼定義字符串時(shí),系統(tǒng)會(huì)使用常量池來管理這個(gè)字符串直接量(會(huì)使用強(qiáng)引用來引用它),系統(tǒng)不會(huì)回收這個(gè)字符串直接量。
import java.lang.ref.PhantomReference; import java.lang.ref.ReferenceQueue; public class PhantomReferenceTest { public static void main(String[] args) throws Exception { //創(chuàng)建一個(gè)字符串對(duì)象 String str = new String("邁阿密熱火"); //創(chuàng)建一個(gè)引用隊(duì)列 ReferenceQueue rq = new ReferenceQueue<>(); //創(chuàng)建一個(gè)虛引用,讓此虛引用引用到"邁阿密熱火"字符串 PhantomReference pr = new PhantomReference(str, rq); //切斷str引用和"邁阿密熱火"字符串之間的引用 str = null; //取出虛引用所引用的對(duì)象,并不能通過虛引用獲取被引用的對(duì)象,所以此處輸出null System.out.println(pr.get()); //① //強(qiáng)制垃圾回收 System.gc(); System.runFinalization(); //垃圾回收之后,虛引用將被放入引用隊(duì)列中 //取出引用隊(duì)列中最先進(jìn)入隊(duì)列的引用于pr進(jìn)行比較 System.out.println(rq.poll() == pr); //② } }
系統(tǒng)無法通過虛引用來獲取被引用的對(duì)象,所以執(zhí)行①處的輸出語句時(shí),程序?qū)⑤敵鰊ull(即使此時(shí)并未強(qiáng)制進(jìn)行垃圾回收)。當(dāng)程序強(qiáng)制垃圾回收后,只有虛引用引用的字符串對(duì)象將會(huì)被垃圾回收,當(dāng)被引用的對(duì)象被回收后,對(duì)應(yīng)的虛引用將被添加到關(guān)聯(lián)的引用隊(duì)列中,因此將在②代碼處看到輸出true。
使用這些引用類可以避免在程序執(zhí)行期間將對(duì)象留在內(nèi)存中。如果以軟引用、弱引用或虛引用的方式引用對(duì)象,垃圾回收器就能夠隨意地釋放對(duì)象。如果希望盡可能減小程序在其生命周期中所占用的內(nèi)存大小時(shí),這些引用類就很有作用。要使用這些引用類,就不能保留對(duì)對(duì)象的強(qiáng)引用;如果保留了對(duì)對(duì)象的強(qiáng)引用,就會(huì)浪費(fèi)這些引用類所提供的任何好處。
//取出弱引用所引用的對(duì)象 obj = wr.get(); //如果取出的對(duì)象為null if (obj == null) { //重新創(chuàng)建一個(gè)新的對(duì)象,再次讓弱引用去引用該對(duì)象 wr = new WeakReference(recreateIt()); //① //取出弱引用所引用的對(duì)象,將其賦給obj變量 obj = wr.get(); //② } ...//操作obj對(duì)象 //再次切斷obj和對(duì)象之間的關(guān)聯(lián) obj = null;
//取出弱引用所引用的對(duì)象 obj = wr.get(); //如果取出的對(duì)象為null if (obj == null) { //重新創(chuàng)建一個(gè)新的對(duì)象,再次強(qiáng)引用去引用該對(duì)象 obj = recreateIt(); //取出弱引用所引用的對(duì)象,將其賦給obj變量 wr = new WeakReference(obj); } ...//操作obj對(duì)象 //再次切斷obj和對(duì)象之間的關(guān)聯(lián) obj = null;
上面兩段偽代碼,其中recreateIt()方法用于生成一個(gè)obj對(duì)象。
第一段代碼存在一定問題:當(dāng)if塊執(zhí)行完成后,obj還是有可能為null。因?yàn)槔厥盏牟淮_定性,假設(shè)系統(tǒng)在①和②行代碼之間進(jìn)行垃圾回收,則系統(tǒng)會(huì)再次將wr所引用的對(duì)象回收,從而導(dǎo)致obj依然為null。
第二段代碼則不會(huì)出現(xiàn)這個(gè)問題,當(dāng)if塊執(zhí)行結(jié)束后,obj一定不為null。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/66317.html
摘要:一次性編譯成機(jī)器碼,脫離開發(fā)環(huán)境獨(dú)立運(yùn)行,運(yùn)行效率較高。解釋型語言使用專門的解釋器對(duì)源程序逐行解釋成特定平臺(tái)的機(jī)器碼并立即執(zhí)行的語言。垃圾回收機(jī)制保護(hù)程序的完整性,垃圾回收是語言安全性策略的一個(gè)重要部分。 Java程序運(yùn)行機(jī)制 編譯型語言 使用專門的編譯器,針對(duì)特定平臺(tái)(操作系統(tǒng))將某種高級(jí)語言源代碼一次性翻譯成可被該平臺(tái)硬件執(zhí)行的機(jī)器碼(包括機(jī)器指令和操作數(shù)),并包裝成該平臺(tái)所能識(shí)...
摘要:棧因?yàn)槭沁\(yùn)行單位,因此里面存儲(chǔ)的信息都是跟當(dāng)前線程相關(guān)的信息?;绢愋秃蛯?duì)象的引用都是在存放在棧中,而且都是幾個(gè)字節(jié)的一個(gè)數(shù),因此在程序運(yùn)行時(shí),他們的處理方式是統(tǒng)一的。對(duì)象,是由基本類型組成的。 一、概念 數(shù)據(jù)類型 java虛擬機(jī)中,數(shù)據(jù)類型可以分為兩類: 基本類型 引用類型 基本類型的變量保存原始值,即:他代表的值就是數(shù)值本身;而引用類型的變量保存引用值?;绢愋桶ǎ篵yte,sh...
摘要:垃圾回收算法與垃圾回收器綜述我們常說的垃圾回收算法可以分為兩部分對(duì)象的查找算法與真正的回收方法。串行垃圾回收器一次只使用一個(gè)線程進(jìn)行垃圾回收并行垃圾回收器一次將開啟多個(gè)線程同時(shí)進(jìn)行垃圾回收。 垃圾回收算法與 JVM 垃圾回收器綜述歸納于筆者的 JVM 內(nèi)部原理與性能調(diào)優(yōu)系列文章,文中涉及的引用資料參考 Java 學(xué)習(xí)與實(shí)踐資料索引、JVM 資料索引。 showImg(https://s...
摘要:本文詳細(xì)描述了堆內(nèi)存模型,垃圾回收算法以及處理內(nèi)存泄露的最佳方案,并輔之以圖表,希望能對(duì)理解內(nèi)存結(jié)構(gòu)有所幫助。該區(qū)域也稱為內(nèi)存模型的本地區(qū)。在中,內(nèi)存泄露是指對(duì)象已不再使用,但垃圾回收未能將他們視做不使用對(duì)象予以回收。 本文詳細(xì)描述了 Java 堆內(nèi)存模型,垃圾回收算法以及處理內(nèi)存泄露的最佳方案,并輔之以圖表,希望能對(duì)理解 Java 內(nèi)存結(jié)構(gòu)有所幫助。原文作者 Sumith Puri,...
閱讀 1724·2021-09-26 09:55
閱讀 3780·2021-09-22 15:31
閱讀 7749·2021-09-22 15:12
閱讀 2239·2021-09-22 10:02
閱讀 4725·2021-09-04 16:40
閱讀 1090·2019-08-30 15:55
閱讀 3069·2019-08-30 12:56
閱讀 1842·2019-08-30 12:44