美團一面:說一說Java中的四種引用類型?

来源:https://www.cnblogs.com/coderacademy/p/18089036
-Advertisement-
Play Games

引言 在JDK1.2之前Java並沒有提供軟引用、弱引用和虛引用這些高級的引用類型。而是提供了一種基本的引用類型,稱為Reference。並且當時Java中的對象只有兩種狀態:被引用和未被引用。當一個對象被引用時,它將一直存在於記憶體中,直到它不再被任何引用指向時,才會被垃圾回收器回收。而被引用也就是 ...


引言

在JDK1.2之前Java並沒有提供軟引用、弱引用和虛引用這些高級的引用類型。而是提供了一種基本的引用類型,稱為Reference。並且當時Java中的對象只有兩種狀態:被引用和未被引用。當一個對象被引用時,它將一直存在於記憶體中,直到它不再被任何引用指向時,才會被垃圾回收器回收。而被引用也就是強引用。

而在JDK1.2之後對引用的概念進行了擴充,分為了強引用(StrongReference)、軟引用(SoftReference)、弱引用(WeakReference)和虛引用(PhantomReference),這4種引用的強度依次減弱。他們的關係如下如:
image.png

強引用

強引用是Java中最常見的引用類型。當你創建一個對象並將其賦值給一個變數時,這個變數會持有該對象的強引用。

Order order = new Order(); // 只要order還指向Order對象,那麼Order對象就不會被回收
order = null; // 強引用都被設置為 null 時,不可達,則Order對象被回收

只要存在強引用指向對象,垃圾回收器將永遠不會回收該對象,即使記憶體不足也不會回收。這可能導致記憶體溢出,因為即使記憶體不足,JVM也不會回收強引用對象。當強引用都被設置為null時,對象變成不可達狀態,垃圾回收器會在適當的時候將其回收。

比如以下示例,我們創建一個2M的數組,但是我們設置JVM參數:-Xms2M -Xmx3M,將JVM的初始記憶體設為2M,最大可用記憶體為3M。

public static void main(String[] args) {  
    //定義一個2M的數組  
    byte[] objects = new byte[1024 * 1024 * 2];  
}

此時我們執行方法後,發現報錯:

image.png
對於強引用,即使記憶體不夠使用,直接報錯OOM,強引用也不會被回收。

對於強引用,就好比生活中,當我們擁有家裡的鑰匙時,我們可以隨時進入你的家,即使我們不需要進入,也能確保我們可以進入。鑰匙是我們進入家的強引用。只有當我們不再擁有鑰匙時,我們才無法進入家,類似於當沒有強引用指向一個對象時,該對象才能被垃圾回收。

軟引用

在JDK1.2之後,用java.lang.ref.SoftReference類來表示軟引用。軟引用允許對象在記憶體不足時被垃圾回收器回收。如果一個對象只有軟引用指向它,當系統記憶體不足時,垃圾回收器會嘗試回收這些對象來釋放記憶體,如果回收了軟引用對象之後仍然沒有足夠的記憶體,才會拋出記憶體溢出異常。軟引用適用於需要緩存大量對象,但又希望在記憶體不足時釋放部分對象以避免記憶體溢出的情況,用於實現緩存時,當記憶體緊張時,可以釋放部分緩存對象以保證系統的穩定性。

以下示例我們設置JVM參數為:-Xms3M -Xmx5M,然後連續創建了10個大小為1M的位元組數組,並賦值給了軟引用,然後迴圈遍歷將這些對象列印出來。

private static final List<Object> list = Lists.newArrayList();  
  
public static void main(String[] args) {  
    IntStream.range(0, 10).forEach(i -> {  
        byte[] buff = new byte[1024 * 1024];  
        SoftReference<byte[]> sr = new SoftReference<>(buff);  
        list.add(sr);  
    });  
  
    System.gc(); // 主動通知垃圾回收  
  
    list.forEach(l -> {  
        Object obj = ((SoftReference<?>) l).get();  
        System.out.println("Object: " + obj);  
    });  
}

然後我們執行代碼之後:

image.png
對於列印結果中,只有最後一個對象保留了下來,其他的obj全都被置空回收了。即說明瞭在記憶體不足的情況下,軟引用將會被自動回收。

對於弱引用,就像我們醫葯箱里的備用藥,當我們需要藥品時,我們會先看看醫葯箱里是否有備用藥。如果醫葯箱里有足夠的藥品(記憶體足夠),我們就可以使用備用藥;但如果醫葯箱里的備用藥不夠了(記憶體不足),我們可能會去藥店購買。在記憶體不足時,垃圾回收器可能會回收軟引用對象,類似於我們在醫葯箱里的備用藥被用完時去藥店購買。

弱引用

JDK1.2之後,用java.lang.ref.WeakReference來表示弱引用。弱引用與軟引用類似,但強度更弱。即使記憶體足夠,只要沒有強引用指向一個對象,垃圾回收器就可以隨時回收該對象。弱引用適用於需要臨時引用對象的場景,如臨時緩存或臨時存儲對象。也可以用於解決對象之間的迴圈引用問題,避免記憶體泄漏。

對於上述示例中,我們將數組賦值給弱引用

private static final List<Object> list = Lists.newArrayList();  
  
public static void main(String[] args) {  
    IntStream.range(0, 10).forEach(i -> {  
        byte[] buff = new byte[1024 * 1024];  
        WeakReference<byte[]> sr = new WeakReference<>(buff);  
        list.add(sr);  
    });  
  
    System.gc(); // 主動通知垃圾回收  
  
    list.forEach(l -> {  
        Object obj = ((WeakReference<?>) l).get();  
        System.out.println("Object: " + obj);  
    });  
}

執行結果發現所有的對象都是null,即都被回收了。

image.png

對於弱引用,就像我們正在旅行,使用一張一次性地圖。我們只在需要導航時使用地圖,一旦旅行結束,我們就不再需要地圖了。這時我們可以選擇扔掉地圖,類似於弱引用,在垃圾回收器運行時,無論記憶體是否充足,對象都可能被回收。

虛引用

在 JDK1.2之後,用java.lang.ref.PhantomReference類來表示虛引用。虛引用是最弱的引用類型,它幾乎對對象沒有任何影響,不能通過虛引用獲取對象,也不能通過它來阻止對象被垃圾回收。從源碼中可以看出它只有一個構造函數和一個 get() 方法,而且它的 get() 方法僅僅是返回一個null,也就是說將永遠無法通過虛引用來獲取對象,虛引用必須要和 ReferenceQueue 引用隊列一起使用。

image.png

虛引用可以用於在對象被回收時進行後續操作,如對象資源釋放或日誌記錄,常用於跟蹤對象被垃圾回收的狀態,執行一些清理工作。

而對於弱引用,就像我們去商店,商店入口處的門閂並不直接影響你進入房屋,但它會在有人進入或離開時發出聲音,提醒你有人進店(歡迎光臨)或者離開(歡迎再來)。類似地,虛引用並不直接影響對象的生命周期,但它可以在對象被回收時發出通知,讓你有機會進行一些後續操作,比如資源釋放或者記錄日誌。

引用隊列

引用隊列(ReferenceQueue)是Java中的一個特殊隊列,用於配合軟引用、弱引用和虛引用,實現更靈活的對象引用和回收管理。

引用隊列的主要作用是跟蹤對象的垃圾回收過程。當一個軟引用、弱引用或虛引用指向的對象被垃圾回收器回收時,如果它們與一個引用隊列關聯,那麼這些引用就會被自動加入到引用隊列中。通過監視引用隊列中的對象,我們可以瞭解到對象的回收狀態,從而執行一些額外的操作,比如資源釋放或日誌記錄等。

總結

Java中的四種引用類型各具特點,可根據程式需求選擇合適的引用類型。強引用保證對象不被意外回收,軟引用和弱引用用於實現緩存或解決記憶體敏感問題,而虛引用則用於對象回收後的通知和清理操作。合理使用引用類型可以更好地管理記憶體和避免記憶體泄漏問題。

本文已收錄於我的個人博客:碼農Academy的博客,專註分享Java技術乾貨,包括Java基礎、Spring Boot、Spring Cloud、Mysql、Redis、Elasticsearch、中間件、架構設計、面試題、程式員攻略等


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 概述:C++結構體的`sizeof`不總是等於每個成員的`sizeof`之和,因為對齊和填充影響了記憶體佈局。未對齊的結構體可能存在間隙,而對齊的結構體會插入填充以保持對齊。通過示例展示了結構體的記憶體對齊和填充,以及如何使用模板元編程列印結構體成員的偏移量,深入理解記憶體佈局。 在C++中,結構體的si ...
  • C++ 語法 讓我們將以下代碼分解以更好地理解它: 示例 #include <iostream> using namespace std; int main() { cout << "Hello World!"; return 0; } 示例解釋 第 1 行:#include <iostream> ...
  • https://leetcode.cn/problems/trapping-rain-water/description/?envType=study-plan-v2&envId=top-interview-150 對於一個可以構成“碗”的序列,最後裝滿水的話應該和最短的一邊齊平,那麼可以左右各遍歷 ...
  • 1 Spark 的 local 模式 Spark 運行模式之一,用於在本地機器上單機模擬分散式計算的環境。在 local 模式下,Spark 會使用單個 JVM 進程來模擬分散式集群行為,所有 Spark 組件(如 SparkContext、Executor 等)都運行在同一個 JVM 進程中,不涉 ...
  • 雲原生技術正重塑IT領域,本文深度剖析了其發展歷程、核心概念、生態系統及實踐案例,展望未來趨勢,揭示了這一技術如何引領企業轉型與創新。 關註【TechLeadCloud】,分享互聯網架構、雲服務技術的全維度知識。作者擁有10+年互聯網服務架構、AI產品研發經驗、團隊管理經驗,同濟本復旦碩,復旦機器人 ...
  • Qt 是一個跨平臺C++圖形界面開發庫,利用Qt可以快速開發跨平臺窗體應用程式,在Qt中我們可以通過拖拽的方式將不同組件放到指定的位置,實現圖形化開發極大的方便了開發效率,本章將重點介紹如何運用`QProcess`組件實現針對進程的控制管理等。當你在使用Qt進行跨平臺應用程式開發時,經常需要與外部進... ...
  • 對於程式員來說這個單詞完全擁有另外一個含義,Spring指的是一個開源項目,而這個項目非常厲害。而Spring與JSR、JarkataEE淵源頗深。 ...
  • 剛做完一道模板A*,看到這題我直接小腦萎縮了... 阿米諾斯!這怎麼用A*?!——剛開題的我 beeeeeeeeee like 甚至比模板簡單(這是綠的...) 其實會是會但是紙張的是這玩意我不會搞估價函數我草! 然後突然想到能不能把這個狀態下有多少個數字不在目標位置作為估價函數? 我喜歡 \(ID ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...