JVM(4)——引用類型

  • 痛點引入:?為什么需要不同的引用類型?直接只用強引用不行嗎?(內存泄漏風險、緩存管理粗粒度、對象生命周期監聽需求)

  • 核心作用:?解釋引用類型如何讓程序員與垃圾收集器(GC)協作,更精細地控制對象的生命周期,影響GC行為。

1. JVM垃圾回收(GC)基礎回顧(簡述)

  • 可達性分析算法(GC Roots)是GC判斷對象是否存活的基礎。

  • 對象從創建到被GC回收的生命周期(強可達 -> ... -> 不可達 -> 回收)。

  • 核心概念:?引用類型直接影響對象在可達性分析鏈條中的“強度”,從而決定GC何時可以回收該對象。

2. 四種引用類型詳解

2.1 強引用
  • 定義:?最常見的引用類型,通過new關鍵字創建的對象默認就是強引用。

  • 語法:?Object obj = new Object();?(obj?就是一個指向新創建Object實例的強引用)

  • 特點:

    • 最強引用:?只要強引用存在(即通過obj能訪問到該對象),GC就絕對不會回收這個對象。

    • 內存泄漏根源:?無意中保持的強引用(如靜態集合長期持有對象、監聽器未注銷)是導致內存泄漏最常見的原因。

  • 如何中斷:?顯式地將引用設置為null?(obj = null;),或者讓引用超出作用域。之后對象變得可被GC回收(但非立即回收)。

2.2 軟引用
  • 定義:?用來描述一些還有用但并非必需的對象。

  • 核心類:?java.lang.ref.SoftReference

  • 語法:

    MyExpensiveObject strongRef = new MyExpensiveObject(); // 強引用創建對象
    SoftReference softRef = new SoftReference<>(strongRef);
    strongRef = null; // 去掉強引用,只剩軟引用
    // 稍后嘗試獲取
    MyExpensiveObject retrieved = (MyExpensiveObject) softRef.get();
    if (retrieved == null) {// 對象已被GC回收,需要重新創建或加載
    }

  • GC行為:

    • 內存充足時,GC不會回收僅被軟引用指向的對象。

    • JVM面臨內存不足(即將發生OOM)?時,GC會嘗試回收這些僅被軟引用指向的對象。

    • 回收發生在Full GC之前(通常是臨近OOM時)。

  • 特點:

    • 內存敏感緩存的理想選擇:?非常適合實現內存敏感的緩存(如圖片緩存、臨時計算結果緩存)。緩存的對象在內存吃緊時會被自動釋放,避免OOM;內存充足時又能提高性能。

    • get()方法:?可能返回null(如果對象已被回收),使用前需檢查。

  • 使用場景:?網頁/圖片緩存、臨時數據緩存、避免重復計算的緩存(計算結果占用內存較大時)。

2.3 弱引用
  • 定義:?用來描述非必需的對象,強度比軟引用更弱。

  • 核心類:?java.lang.ref.WeakReference

  • 語法:

    MyObject strongRef = new MyObject();
    WeakReference weakRef = new WeakReference<>(strongRef);
    strongRef = null; // 去掉強引用,只剩弱引用
    // 嘗試獲取 (很可能馬上為null)
    MyObject retrieved = weakRef.get(); // 可能返回null

  • GC行為:

    • 無論當前內存是否充足,只要發生垃圾回收(即使是Minor GC),并且對象僅被弱引用指向(沒有強引用、軟引用),那么這個對象就會被回收。

    • 回收具有不確定性,隨時可能發生。

  • 特點:

    • 生命周期極短:?一旦失去強引用,對象在下一次GC時幾乎肯定被回收。

    • get()方法:?同樣可能返回null

    • 防止內存泄漏的關鍵:?經典應用在規范映射(Canonicalizing Mappings)?和監聽器/回調場景中,避免因持有對方引用而導致雙方都無法被回收。WeakHashMap是其典型代表(Key是弱引用)。

  • 使用場景:

    • WeakHashMap(Key弱引用):常用于實現類元信息緩存、監聽器列表(防止監聽器無法被回收導致內存泄漏)。

    • 輔助性信息的關聯(當主要對象被回收時,輔助信息也應自動釋放)。

    • ThreadLocal的內部實現ThreadLocalMapEntry繼承了WeakReference(Key是弱引用指向ThreadLocal對象),目的是防止ThreadLocal對象本身因線程長時間存活(如線程池)而無法被回收。

2.4 虛引用
  • 定義:?也稱為“幽靈引用”或“幻象引用”,是最弱的一種引用關系。

  • 核心類:?java.lang.ref.PhantomReference

  • 語法:

    ReferenceQueue queue = new ReferenceQueue<>();
    MyResource resource = new MyResource(); // 可能持有Native資源
    PhantomReference phantomRef = new PhantomReference<>(resource, queue);
    resource = null; // 去掉強引用
    // ... 稍后
    // 無法通過 phantomRef.get() 獲取對象,它永遠返回 null!
    // 監控隊列
    Reference<? extends MyResource> refFromQueue = queue.poll();
    if (refFromQueue != null) {// 對象已被回收,且進入了引用隊列// 在這里執行資源清理操作 (如關閉文件句柄、釋放Native內存)refFromQueue.clear(); // 徹底斷開虛引用
    }

  • GC行為:

    • 完全不影響對象的生命周期。如果一個對象僅被虛引用指向,那么它和沒有引用指向一樣,GC會隨時回收它。

    • 關鍵區別:?虛引用必須ReferenceQueue聯合使用。

  • 特點:

    • get()方法永遠返回null!不能通過虛引用來獲取對象實例。

    • 唯一作用:?在對象被GC回收后,GC會將其關聯的虛引用對象放入引用隊列。程序通過監控這個隊列,可以精確知道對象何時被回收

    • 對象回收后的通知機制:?這是虛引用的核心價值。

  • 使用場景:

    • 精準的資源清理:?主要用于在對象被GC回收后,執行一些非常重要的、與Java對象本身無關的資源釋放操作。典型例子是管理堆外內存(Direct ByteBuffer的Cleaner機制內部就使用了PhantomReference)或文件句柄。finalize()方法不可靠且已被廢棄,虛引用+引用隊列是更好的替代方案。

    • 對象回收的監控/日志。

3. 引用隊列

  • 作用:?與軟引用、弱引用、虛引用配合使用。當引用指向的對象被GC回收后,JVM會(在某個不確定的時間點)將引用對象本身(即SoftReference/WeakReference/PhantomReference實例)放入這個隊列。

  • 核心類:?java.lang.ref.ReferenceQueue

  • 工作原理:

    1. 創建引用時關聯一個ReferenceQueue

    2. 當引用指向的對象被GC回收后,JVM將這個引用對象(不是被回收的對象)放入隊列。

    3. 程序通過輪詢poll()或阻塞remove()方法從隊列中取出引用對象。

    4. 取出的引用對象可以:

      • 清理操作:?(虛引用主要場景) 執行關聯的清理邏輯(如釋放Native資源)。

      • 移除引用:?從一些管理容器中移除該引用,防止引用對象本身堆積造成內存浪費(例如WeakHashMap會利用隊列清理失效的Entry)。

  • 重要性:?是實現“對象回收后動作”的關鍵橋梁。軟/弱引用不一定要搭配隊列,但虛引用必須搭配隊列才有意義。

4. 對比總結與選擇指南

特性強引用軟引用弱引用虛引用
強度最強最弱 (或無)
GC影響絕不回收內存不足時回收 (OOM前)發現即回收 (下次GC)不影響回收 (隨時可回收)
get()返回對象內存足返回對象;內存不足被回收則返回null未被回收返回對象;被回收則返回null永遠返回?null
隊列可選可選必須
主要用途對象默認引用內存敏感緩存防止內存泄漏?(規范映射, 監聽器清理)對象回收后通知與資源清理?(替代finalize)
典型類所有new對象SoftReferenceWeakReference,?WeakHashMap?(Key)PhantomReference?(必須配ReferenceQueue)
回收時機顯式斷鏈后 (可被回收)內存不足時下次GC發生時隨時 (回收后入隊通知)
  • 選擇指南:

    • 需要對象一直存在 ->?強引用?(注意及時置null)

    • 緩存對象,希望內存不足時自動釋放 ->?軟引用?(配合或不配合隊列)

    • 關聯輔助數據/監聽器,主要對象回收時自動解除關聯 ->?弱引用?(常配合WeakHashMap或隊列)

    • 需要精確知道對象被回收的時機并執行關鍵清理(尤其是非Java資源)->?虛引用?(必須配合ReferenceQueue)

5. 實戰應用與代碼示例?

  • 軟引用實現簡單內存緩存:

    public class ImageCache {private final Map cache = new HashMap<>();private final ReferenceQueue queue = new ReferenceQueue<>();public void putImage(String key, BufferedImage image) {// 清理隊列中已被GC回收的軟引用cleanupQueue();// 創建軟引用并放入緩存,關聯引用隊列SoftReference ref = new SoftReference<>(image, queue);cache.put(key, ref);}public BufferedImage getImage(String key) {cleanupQueue(); // 先清理SoftReference ref = cache.get(key);if (ref != null) {BufferedImage image = ref.get();if (image != null) {return image; // 緩存命中} else {cache.remove(key); // 引用還在但對象已被回收,移除無效條目}}return null; // 緩存未命中或失效}private void cleanupQueue() {Reference<? extends BufferedImage> ref;while ((ref = queue.poll()) != null) {// 找到隊列中的引用,并從緩存Map中移除對應的鍵(需要設計鍵與引用的關聯)// 通常需要額外設計數據結構(如WeakReference<Key>)來找到對應的key,這里簡化處理// 更常見的做法是使用WeakHashMap或Guava Cache等成熟庫cache.values().removeIf(value -> value == ref); // 效率不高,僅示意}}
    }

  • 弱引用防止內存泄漏 (WeakHashMap?示例):

    public class ListenerManager {private final Map listeners = new WeakHashMap<>();public void addListener(EventListener listener, Object source) {// Key (listener) 是弱引用。如果listener外部沒有強引用了,它會被GC回收,Entry也會自動移除listeners.put(listener, source);}// 當listener對象在其他地方沒有強引用時,它會被GC回收,WeakHashMap會自動移除對應的Entry// 無需顯式調用removeListener,避免了因忘記注銷導致的內存泄漏
    }

  • 虛引用管理堆外內存 (模擬?DirectByteBuffer?的?Cleaner?機制):

    public class NativeResourceHolder {private final long nativeHandle; // 假設代表Native資源指針private final Cleaner cleaner;public NativeResourceHolder() {this.nativeHandle = allocateNativeResource(); // 分配Native資源this.cleaner = Cleaner.create(this, new ResourceCleaner(nativeHandle));}// 內部靜態類,執行實際的清理工作private static class ResourceCleaner implements Runnable {private final long handleToClean;ResourceCleaner(long handle) {this.handleToClean = handle;}@Overridepublic void run() {// 確保在PhantomReference入隊后被調用,釋放Native資源freeNativeResource(handleToClean);System.out.println("Native resource freed for handle: " + handleToClean);}}// Native方法(示意)private native long allocateNativeResource();private native void freeNativeResource(long handle);
    }
    // Cleaner內部簡化原理 (JDK實際實現更復雜):
    public class Cleaner {private final PhantomReference<Object> phantomRef;private final Runnable cleanupTask;private static final ReferenceQueue<Object> queue = new ReferenceQueue<>();private Cleaner(Object referent, Runnable task) {cleanupTask = task;phantomRef = new PhantomReference<>(referent, queue);// 通常會啟動一個守護線程監控queue}public static Cleaner create(Object obj, Runnable task) {return new Cleaner(obj, task);}// 守護線程大致邏輯static {Thread cleanerThread = new Thread(() -> {while (true) {try {Reference<?> ref = queue.remove(); // 阻塞等待if (ref instanceof CleanerPhantomReference) {((CleanerPhantomReference) ref).clean();}} catch (InterruptedException e) { /* ... */ }}});cleanerThread.setDaemon(true);cleanerThread.start();}private static class CleanerPhantomReference extends PhantomReference<Object> {private final Runnable task;CleanerPhantomReference(Object referent, ReferenceQueue<? super Object> q, Runnable task) {super(referent, q);this.task = task;}void clean() {task.run();}}
    }

6. 常見陷阱與最佳實踐

  • 誤用強引用:?最常見的泄漏原因(靜態集合、未注銷的監聽器、緩存設計不當)。時刻警惕對象的生命周期。

  • 軟引用/弱引用緩存不檢查get()?拿到null后未正確處理,導致邏輯錯誤或NPE。使用前務必判空。

  • 濫用軟引用:?將所有緩存都用軟引用,可能導致緩存命中率低(頻繁被回收)或回收不及時(內存壓力大時集中回收導致卡頓)。評估對象價值和內存占用。

  • 弱引用導致過早回收:?如果弱引用的對象還在使用中(有強引用鏈),但某個關鍵的弱引用被回收了(例如WeakHashMap的Key),可能導致意外行為。理解WeakHashMap?Key被回收的影響。

  • 虛引用忘記關聯隊列或忘記處理隊列:?虛引用失去意義。必須配合隊列并主動輪詢/處理。

  • 引用對象本身的內存泄漏:?如果不斷創建軟/弱/虛引用對象(例如在緩存中),卻不清理隊列或管理容器,這些引用對象本身會堆積占用內存。及時清理引用隊列中的失效引用。

  • 優先使用成熟緩存庫:?如Caffeine,?Guava Cache等,它們內部精細地處理了引用類型、隊列、并發和過期策略,比自己實現更健壯高效。

  • 理解finalize()的弊端:?它不可靠、性能差、可能導致對象復活,已被廢棄。優先考慮虛引用+隊列進行資源清理。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/85613.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/85613.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/85613.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

ONLYOFFICE 文檔 9.0 版本已發布:新界面、圖表查看器、.md 文件支持、AI 表格與宏等更新

ONLYOFFICE 文檔 9.0 版本已正式發布。此次更新包含 20 多項新功能和約 500 項修復&#xff0c;全面提升您的辦公效率。從全新界面、突破性的 AI 工具到更廣泛的文件格式兼容性&#xff0c;本次發布將帶來更加流暢的使用體驗。閱讀本文&#xff0c;了解詳情。 更新全部編輯器的…

關于python-socket服務的問題記錄

概述 在使用pythonwebsocket部署socket服務&#xff0c;前端使用小程序來連接&#xff0c;過程中存在以下可能出現的問題&#xff1a; 1&#xff0c;代碼里socket端口問題2&#xff0c;服務器配置問題&#xff08;域名解析&#xff1f;Nginx配置是否正確處理了WebSocket升級頭…

typescript vs go vs rust

typescript 后端選型&#xff1a; Express &Typescript &trpc 廣泛使用&#xff0c;靈活&#xff0c;快速&#xff0c;穩定 Nestjs 企業級&#xff0c;標準化&#xff0c;像java &#xff0c;依賴注入&#xff0c; Hono , web standards framework. Support for any J…

OpenGL和OpenGL ES區別

OpenGL&#xff08;Open Graphics Library&#xff09;和OpenGL ES&#xff08;OpenGL for Embedded Systems&#xff09;都是用于圖形渲染的API&#xff0c;但它們的目標平臺和設計定位有所不同。 1. 目標平臺 OpenGL 主要用于桌面平臺&#xff08;如Windows、macOS、Linux&a…

PyTorch 入門之官方文檔學習筆記(一)

目錄 1 張量 1&#xff09;張量的初始化和屬性 2&#xff09;張量操作 3&#xff09;使用 NumPy 進行橋接 2 torch.autograd 1&#xff09;背景 2&#xff09;在 PyTorch 中的使用 3&#xff09;Autograd 的微分機制 4&#xff09;計算圖原理 5&#xff09;從計算圖中…

King’s LIMS 系統引領汽車檢測實驗室數字化轉型

隨著汽車保有量的持續攀升和車齡的增長&#xff0c;消費者對汽車的需求已悄然轉變&#xff0c;從最初對外觀和性能的追求&#xff0c;逐漸深化為對安全性、可靠性、耐久性、性能與舒適性以及智能化功能的全方位關注。這無疑讓汽車檢測行業在保障車輛質量、滿足市場需求方面肩負…

Neo4j常見語句-merge

merge用法&#xff1a;MERGE 是 Neo4j 中一個強大的操作符&#xff0c;用于確保圖中存在特定的節點或關系。它的核心邏輯是&#xff1a;如果目標存在則匹配&#xff0c;不存在則創建 基本語法與邏輯&#xff1a; MERGE <pattern> [ON CREATE <create_clause>] //…

Mem0多級記憶實現機制詳解

在人工智能交互場景中,記憶能力是實現個性化服務與智能決策的關鍵。Mem0 通過設計分層記憶架構,實現了對用戶、會話和智能體狀態的多級管理。各層級記憶既相互獨立存儲,又通過精密的關聯機制協同運作,確保在不同場景下都能提供精準的上下文支持,顯著提升 AI 交互的智能性與…

Python 爬蟲入門 Day 5 - 使用 XPath 進行網頁解析(lxml + XPath)

Python 第二階段 - 爬蟲入門 &#x1f3af; 今日目標 掌握 XPath 的基本語法使用 lxml.etree 解析 HTML&#xff0c;提取數據與 BeautifulSoup 比較&#xff1a;誰更強&#xff1f; &#x1f4d8; 學習內容詳解 ? 安裝依賴 pip install lxml&#x1f9e9; XPath 簡介 XPa…

變幻莫測:CoreData 中 Transformable 類型面面俱到(六)

概述 各位似禿似不禿小碼農們都知道&#xff0c;在蘋果眾多開發平臺中 CoreData 無疑是那個最簡潔、擁有“官方認證”且最具兼容性的數據庫框架。使用它可以讓我們非常方便的搭建出 App 所需要的持久存儲體系。 不過&#xff0c;大家是否知道在 CoreData 中還存在一個 Transfo…

Vuex(一) —— 集中式的狀態管理倉庫

目錄 Vue組件間通信方式回顧 組件內的狀態管理流程組件間通信方式 父組件給子組件傳值 (最簡單的一種方式)子組件給父組件傳值不相關組件之間傳值其他常見方式($ref) 簡易的狀態管理方案 上面組件間通信方式的問題集中式的狀態管理方案 Vuex 什么是Vuex?什么情況下使用Vuex?…

操作系統---內存管理上

文章目錄 1. 內存的基礎知識1.1 什么是內存&#xff0c;有何作用1.2 進程運行的基本原理1.2.1 指令的工作原理1.2.2 邏輯地址 VS 物理地址 1.3 如何實現地址轉換&#xff08;邏輯 -> 物理&#xff09;1.3.1 絕對裝入1.3.2 可重定位裝入&#xff08;靜態重定位&#xff09;1.…

醫學圖像處理期末復習

目錄 考試范圍第1章 緒論1.1 數字圖像處理的概念1.2 數字圖像處理的應用領域1、醫學領域2、其他領域 1.3 數字圖像處理基礎1.4 數字圖像基礎運算 第2章 醫學圖像灰度變換與空間濾波2.1 醫學圖像灰度變換線性灰度變換非線性灰度變換 2.2 直方圖均衡化√2.3 空間平滑濾波線性空間…

類圖:軟件世界的“建筑藍圖”

本文來自「大千AI助手」技術實戰系列&#xff0c;專注用真話講技術&#xff0c;拒絕過度包裝。 類圖&#xff08;Class Diagram&#xff09;&#xff1a;軟件世界的“建筑藍圖” 類圖&#xff08;Class Diagram&#xff09;是統一建模語言&#xff08;UML&#xff09; 中最重要…

利用DevEco Studio對RK3588的HiHopesOS-4.1.110(OpenHarmony)進行Qt程序編寫

文章目錄 熱身準備添加Qt庫運行qml程序 熱身 可以先看我這篇文章【DevEco Studio中使用Qt&#xff0c;編寫HarmonyOS程序】 準備 板子的主要信息 目前由于系統版本&#xff08;API 11&#xff09;及其他原因&#xff0c;只能用4.1版本的DevEcoStudio來編寫&#xff0c;更高…

設計模式精講 Day 5:原型模式(Prototype Pattern)

【設計模式精講 Day 5】原型模式&#xff08;Prototype Pattern&#xff09; 文章內容 在“設計模式精講”系列的第5天&#xff0c;我們將深入講解原型模式&#xff08;Prototype Pattern&#xff09;。作為創建型設計模式之一&#xff0c;原型模式通過復制已有對象來創建新對…

深度學習——第2章習題2-1分析為什么平方損失函數不適用于分類問題

深度學習——第2章習題2-1 《神經網絡與深度學習》——邱錫鵬 2-1 分析為什么平方損失函數不適用于分類問題。 平方損失函數&#xff08;Quadratic Loss Function&#xff09;經常用在預測標簽y為實數值的任務中&#xff0c;定義為 L ( y , f ( x ; θ ) ) 1 2 ( y ? f (…

【Linux】運行腳本后打屏同時保存到本地

命令&#xff1a; sh run.sh 2>&1 | tee output.log sh run.sh 2>&1 | tee output_$(date "%Y%m%d%H%M").log作用&#xff1a;運行腳本&#xff0c;并同時將輸出&#xff08;包括標準輸出和錯誤輸出&#xff09;顯示到終端&#xff0c;并保存到文件中…

Spark 在小眾日常場景中的實戰應用:從小店數據到社區活動

Spark 在小眾日常場景中的實戰應用&#xff1a;從小店數據到社區活動? 提起 Spark&#xff0c;大家往往會聯想到大型互聯網公司的數據處理、金融行業的復雜分析。但實際上&#xff0c;Spark 在許多小眾、貼近生活的場景中也能大顯身手。結合學習與實踐&#xff0c;我探索了 S…

mysql 執行計劃 explain命令 詳解

explain id &#xff1a;select查詢的序列號&#xff0c;包含一組數字&#xff0c;表示查詢中執行select子句或操作表的順序select_type&#xff1a;查詢類型 或者是 其他操作類型table &#xff1a;正在訪問哪個表partitions &#xff1a;匹配的分區type &#xff1a;訪問的類…