美團一面:說一說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
  • 示例項目結構 在 Visual Studio 中創建一個 WinForms 應用程式後,項目結構如下所示: MyWinFormsApp/ │ ├───Properties/ │ └───Settings.settings │ ├───bin/ │ ├───Debug/ │ └───Release/ ...
  • [STAThread] 特性用於需要與 COM 組件交互的應用程式,尤其是依賴單線程模型(如 Windows Forms 應用程式)的組件。在 STA 模式下,線程擁有自己的消息迴圈,這對於處理用戶界面和某些 COM 組件是必要的。 [STAThread] static void Main(stri ...
  • 在WinForm中使用全局異常捕獲處理 在WinForm應用程式中,全局異常捕獲是確保程式穩定性的關鍵。通過在Program類的Main方法中設置全局異常處理,可以有效地捕獲並處理未預見的異常,從而避免程式崩潰。 註冊全局異常事件 [STAThread] static void Main() { / ...
  • 前言 給大家推薦一款開源的 Winform 控制項庫,可以幫助我們開發更加美觀、漂亮的 WinForm 界面。 項目介紹 SunnyUI.NET 是一個基於 .NET Framework 4.0+、.NET 6、.NET 7 和 .NET 8 的 WinForm 開源控制項庫,同時也提供了工具類庫、擴展 ...
  • 說明 該文章是屬於OverallAuth2.0系列文章,每周更新一篇該系列文章(從0到1完成系統開發)。 該系統文章,我會儘量說的非常詳細,做到不管新手、老手都能看懂。 說明:OverallAuth2.0 是一個簡單、易懂、功能強大的許可權+可視化流程管理系統。 有興趣的朋友,請關註我吧(*^▽^*) ...
  • 一、下載安裝 1.下載git 必須先下載並安裝git,再TortoiseGit下載安裝 git安裝參考教程:https://blog.csdn.net/mukes/article/details/115693833 2.TortoiseGit下載與安裝 TortoiseGit,Git客戶端,32/6 ...
  • 前言 在項目開發過程中,理解數據結構和演算法如同掌握蓋房子的秘訣。演算法不僅能幫助我們編寫高效、優質的代碼,還能解決項目中遇到的各種難題。 給大家推薦一個支持C#的開源免費、新手友好的數據結構與演算法入門教程:Hello演算法。 項目介紹 《Hello Algo》是一本開源免費、新手友好的數據結構與演算法入門 ...
  • 1.生成單個Proto.bat內容 @rem Copyright 2016, Google Inc. @rem All rights reserved. @rem @rem Redistribution and use in source and binary forms, with or with ...
  • 一:背景 1. 講故事 前段時間有位朋友找到我,說他的窗體程式在客戶這邊出現了卡死,讓我幫忙看下怎麼回事?dump也生成了,既然有dump了那就上 windbg 分析吧。 二:WinDbg 分析 1. 為什麼會卡死 窗體程式的卡死,入口門檻很低,後續往下分析就不一定了,不管怎麼說先用 !clrsta ...
  • 前言 人工智慧時代,人臉識別技術已成為安全驗證、身份識別和用戶交互的關鍵工具。 給大家推薦一款.NET 開源提供了強大的人臉識別 API,工具不僅易於集成,還具備高效處理能力。 本文將介紹一款如何利用這些API,為我們的項目添加智能識別的亮點。 項目介紹 GitHub 上擁有 1.2k 星標的 C# ...