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