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