四種引用類型在JAVA Springboot中的使用詳解

概念介紹

不同的引用類型,主要體現的是對象不同的可達性(reachable)狀態和對垃圾收集的影響。

01.  強引用

這個就是我們創建的普通對象瞭~ 當該對象被顯示地賦值為 null 時,或者沒有被其他存活的對象繼續引用時,它就會成為垃圾收集器的目標,等待被收回

02.  軟引用

軟引用( SoftReference ) , 當內存不足 時會被回收

比如

image-20210620220531336

被回收後,這裡會打印 null 而不是 Java4ye

03.  弱引用

弱引用( WeakReference ) , 當 垃圾回收器 進行垃圾回收時,無論內存足與否,它都會被垃圾回收器回收

比如

image-20210620221353830

被回收後,這裡也是會打印 null 而不是 Java4ye

04.  虛引用

虛引用( ReferenceQueue ) , 這個也是隨時會被回收,不過它的作用更像一個標記,當對象被回收時,它不為 null ,但是要註意,無論什麼時候去調用 虛引用的 get 方法,都隻能獲取到一個 null 值。

為一個對象設置虛引用關聯的唯一目的就是能在這個對象被收集器回收時收到一個系統通知 —— <<深入理解Java虛擬機>>

這裡引用 http://www.javashuo.com/article/p-zyvdcbhl-nb.html 該文章的例子

User user = new User(1, "Java4ye");
ReferenceQueue<User> userReferenceQueue = new ReferenceQueue<>();
// 創建User對象的虛引用
PhantomReference<User> phantomReference = new PhantomReference<>(user, userReferenceQueue);
// 去掉強引用
user = null;
System.out.println(phantomReference.get());
// 手動觸發GC
System.gc();
System.out.println("GC: " + phantomReference.get());
Reference<? extends User> reference = null;
try {
    reference = userReferenceQueue.remove(1000);
} catch (InterruptedException e) {
    e.printStackTrace();
}
if (reference != null) {
    System.out.println("對象User被回收瞭:");
}

對象可達性

那麼 簡單介紹完上面的 4 種引用後,我們再來看看它的可達性~

如圖~

image-20210621083333189

  • 強可達: 比如 創建一個對象時,創建它的線程對該對象就是強可達
  • 軟可達: 隻能通過軟引用訪問
  • 弱可達: 隻能通過弱引用訪問
  • 虛可達: 當對象沒有 強,軟,弱 引用關聯時,並且 finalize 過,就會進入該狀態
  • 不可達: 意味著該對象可以被清除瞭。

通過最開始的代碼例子和上面的圖(雙向箭頭)我們還可以發現,軟引用和弱引用和強引用這三者間可以進行轉換( 通過 Reference 的 get() 可獲取到原對象),這意味著:

對於軟引用、弱引用之類,垃圾收集器可能會存在二次確認的問題,以保證處於弱引用狀態的對象,沒有改變為強引用。

在 JDK8 中,還可以通過 指定參數打印引用的相關信息

-XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintReferenceGC

 JDK8 中使用 ParrallelGC 收集的垃圾回收日志 (大佬 pdf 中的例子)

0.403: [GC (Allocation Failure) 0.871: [SoftReference, 0 refs, 0.0000393 secs]0.871: [WeakReference, 8 refs, 0.0000138 secs]0.871: [FinalReference, 4 refs, 0.0000094 secs]0.871:

[PhantomReference, 0 refs, 0 refs, 0.0000085 secs]0.871: [JNI Weak Reference, 0.0000071 secs][PSYoungGen: 76272K->10720K(141824K)] 128286K->128422K(316928K), 0.4683919 secs] [Times:

user=1.17 sys=0.03, real=0.47 secs]

再記錄下這個點👇

通過底層API來達到強引用👍

image-20210621085544098

Springboot源碼中的使用

嘿嘿 終於來到重點瞭 ,正如開頭提到的~ 4ye 也是在 Springboot 源碼中看到這個

ConcurrentReferenceHashMap 才想起要寫一下這篇文章滴✍

那麼這個 ConcurrentReferenceHashMap 到底有什麼作用呢?

ConcurrentReferenceHashMap 能指定所存放對象的引用級別

默認情況下是 軟引用級別

image-20210621122940290

比如 在 Springboot自動裝配原理探索 一文中提到的 Springboot SPI 機制 其中的主角: SpringFactoriesLoader

源碼如下:

image-20210621092032074

還有自動配置過程中的註解掃描 AnnotationsScanner

image-20210621093124042

以及在 萬字長文,帶你快速上手這些池化技術! 一文中出現的 異步任務線程池 ThreadPoolTaskExecutor

源碼如下: (可以看到這裡指明瞭是 弱引用級別)

image-20210621092723147

總結

看完上面的例子,覺得可以模仿下 Springboot 的 ConcurrentReferenceHashMap ,對對象進行一個合理的存儲,間接地優化jvm ,提高垃圾回收的效率。這兩個別搞混瞭: 軟引用,內存不足時回收;弱引用,在進行垃圾回收時,不管內存足與否,都會被回收

本篇文章就到這裡瞭,希望能夠給你帶來幫助,也希望您能夠多多關註WalkonNet的更多內容!

推薦閱讀: