1.如何判斷對象是否存活
1.1 引用計數算法
????????概念:在對象頭部增加一個引用計數器,每當有一個地方引用它時,計數器值就加一;當引用失效時,計數器值就減一;任何時刻計數器為零的對象就是不可能再被使用的。
????????優點:速度快,高效
????????缺點: 假如此時A引用B,B引用A那么,計數永遠不為0,那么永遠無法回收他們
1.2 可達性分析算法:
????????概念:通過一系列稱為“GC Roots”的根對象作為起始節點集,從這些節點開始,根據引用關系向下搜索,搜索過程所走過的路徑稱為“引用鏈”(Reference Chain),如果某個對象到GC Roots間沒有任何引用鏈相連,或者用圖論的話來說就是從GC Roots到這個對象不可達時,則證明此對象是不可能再被使用的。
? ? ? ?總結概念: 也就是找到是否連接根節點,此時如果A引用B,B引用A就不會發生,循環回收不掉的問題,因為他們找不到連接的根節點,也是當前主流的判斷對象是否存活的算法
????????注意:即使在可達性分析算法中判定為不可達的對象,也不是“非死不可”的,這時候它們暫時還處于“緩刑”階段,要真正宣告一個對象死亡,至少要經歷兩次標記過程:如果對象在進行可達性分析后發現沒有與GC Roots相連接的引用鏈,那它將會被第一次標記,隨后進行一次篩選,篩選的條件是此對象是否有必要執行finalize()方法。
2.再談引用
????????無論是通過引用計數算法判斷對象的引用數量,還是通過可達性分析算法判斷對象是否引用鏈可 達,判定對象是否存活都和“ 引用 ” 離不開關系。????????在JDK 1.2 版之前, Java 里面的引用是很傳統的定義: 如果reference 類型的數據中存儲的數值代表的是另外一塊內存的起始地址,就稱該 reference 數據是代表 某塊內存、某個對象的引用。這種定義并沒有什么不對,只是現在看來有些過于狹隘了,一個對象在 這種定義下只有“ 被引用 ” 或者 “ 未被引用 ” 兩種狀態,對于描述一些 “ 食之無味,棄之可惜 ” 的對象就顯 得無能為力。????????譬如我們希望能描述一類對象:當內存空間還足夠時,能保留在內存之中,如果內存空間在進行垃圾收集后仍然非常緊張,那就可以拋棄這些對象 —— 很多系統的緩存功能都符合這樣的應用場景。? ? ? ? 所以在JDK 1.2版之后,Java對引用的概念進行了擴充,引入了四種引用
3.四種引用
2.1? 強引用
????????是最傳統的“引用”的定義,是指在程序代碼之中普遍存在的引用賦值,即類似“Object obj=new Object()”這種引用關系。無論任何情況下,只要強引用關系還存在,垃圾收集器就永遠不會回收掉被引用的對象。
2.2 軟引用
????????是用來描述一些還有用,但非必須的對象。只被軟引用關聯著的對象,在系統將要發生內存溢出異常前,會把這些對象列進回收范圍之中進行第二次回收,如果這次回收還沒有足夠的內存,才會拋出內存溢出異常。在JDK 1.2版之后提供了SoftReference類來實現軟引用。
2.3 弱引用
????????也是用來描述那些非必須對象,但是它的強度比軟引用更弱一些,被弱引用關聯的對象只能生存到下一次垃圾收集發生為止。當垃圾收集器開始工作,無論當前內存是否足夠,都會回收掉只被弱引用關聯的對象。在JDK 1.2版之后提供了WeakReference類來實現弱引用。
2.4 虛引用
????????也稱為“幽靈引用”或者“幻影引用”,它是最弱的一種引用關系。一個對象是否有虛引用的存在,完全不會對其生存時間構成影響,也無法通過虛引用來取得一個對象實例。為一個對象設置虛引用關聯的唯一目的只是為了能在這個對象被收集器回收時收到一個系統通知。在JDK 1.2版之后提供了PhantomReference類來實現虛引用。
3. 軟引用的進一步了解
3.1 軟引用的使用場景?
?????????軟引用主要用于實現內存敏感的高速緩存。當系統內存充足時,軟引用的對象可以長時間駐留在內存中,供程序快速訪問;當系統內存不足時,這些對象可以被垃圾回收器回收,從而避免內存溢出錯誤。這種機制非常適合用于緩存系統,如圖片緩存、頁面緩存等。
3.2 為什么用到了軟引用?
內存壓力下的靈活應對:軟引用允許JVM在內存緊張時自動回收對象,從而避免了程序因為內存不足而崩潰的風險。
提高性能:通過保留軟引用對象,在內存充足時可以直接訪問這些對象,減少了重新加載或重新計算這些對象所需的時間和資源。
資源管理的靈活性:開發者可以通過軟引用來控制緩存的大小和生命周期,而不是完全依賴于JVM的垃圾回收機制。
3.3 軟引用的使用(小例子)
import java.lang.ref.SoftReference; public class SoftReferenceExample { public static void main(String[] args) { // 假設這是我們需要緩存的數據 Object data = new Object(); // 創建一個軟引用指向這個數據 SoftReference<Object> softRef = new SoftReference<>(data); // 使用softRef.get()來獲取軟引用指向的對象 Object cachedData = softRef.get(); // 模擬內存緊張的情況(實際上在簡單示例中無法直接模擬) // 在實際環境中,當JVM進行垃圾回收時,如果內存不足,可能會回收softRef指向的對象 // 再次嘗試獲取對象,如果對象已被回收,則返回null cachedData = softRef.get(); if (cachedData != null) { // 對象仍然存在,可以繼續使用 } else { // 對象已被回收,需要重新加載或重新計算 } } }
注意:由于垃圾回收的時機和過程是由JVM的垃圾回收器控制的,因此我們不能精確地預測軟引用對象何時會被回收。在實際應用中,開發者需要根據應用的內存需求和性能要求來合理使用軟引用。
4. 弱引用的進一步了解
4.1 使用場景
- 緩存系統:
- 在一些緩存系統中,特別是當緩存的數據量很大且不是必需時,可以使用弱引用來引用這些數據。這樣,當JVM內存不足時,這些數據可以被自動回收,從而避免內存溢出。
- 例如,
WeakHashMap
就是基于弱引用實現的,它允許鍵值對中的鍵是弱引用的。這意味著,如果某個鍵除了被WeakHashMap
所引用外,沒有其他強引用指向它,那么這個鍵以及它所對應的值都可能被垃圾回收器回收。- 生命周期較短的臨時對象:
- 對于那些生命周期較短,且對內存敏感的臨時對象,可以使用弱引用來引用它們。這樣,一旦這些對象不再被其他強引用所指向,它們就可以被垃圾回收器及時回收。
4.2?為什么用到了弱引用
- 內存管理的靈活性:
- 弱引用提供了一種比軟引用更加靈活的內存管理方式。通過弱引用,開發者可以更加精確地控制對象的生命周期,避免內存泄漏,同時也能夠確保在內存不足時能夠自動回收非必需的對象。
- 減少內存泄漏的風險:
- 在一些復雜的應用程序中,由于對象之間的引用關系錯綜復雜,很容易出現內存泄漏的情況。通過使用弱引用,可以減少因為對象之間的強引用關系而導致的內存泄漏風險。
- 提升性能:
- 在一些性能敏感的應用程序中,及時回收不再使用的對象可以釋放更多的內存空間,從而提升應用程序的性能。弱引用通過自動回收非必需的對象,有助于減少內存占用,提升性能。
4.3 弱引用的使用(小例子)
import java.lang.ref.WeakReference; public class WeakReferenceExample { public static void main(String[] args) { // 創建一個強引用對象 Object strongRef = new Object(); // 創建一個弱引用對象,指向強引用對象 WeakReference<Object> weakRef = new WeakReference<>(strongRef); // 驗證弱引用是否有效 System.out.println(weakRef.get()); // 輸出對象地址 // 將強引用置為null,模擬對象不再被強引用 strongRef = null; // 觸發垃圾回收(注意:實際環境中,垃圾回收的時機是不確定的) System.gc(); // 再次驗證弱引用是否有效 // 由于強引用已被置為null,且沒有其他強引用指向該對象,因此該對象可能會被垃圾回收 // 此時,weakRef.get()可能返回null System.out.println(weakRef.get()); // 可能輸出null } }
同樣和弱引用相同:由于垃圾回收的時機和過程是由JVM的垃圾回收器控制的,因此我們不能精確地預測弱引用對象何時會被回收。在實際應用中,開發者需要根據應用的內存需求和性能要求來合理使用弱引用。