自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

讓人心疼的Java虛引用!

開(kāi)發(fā) 后端
在Java的世界里,對(duì)象的存在層次,也有三六九等,充滿了階層之間的嘲弄。強(qiáng)軟弱虛各種引用,對(duì)于熟悉Java的同學(xué)一定不會(huì)感到陌生,它們隨著等級(jí)的降低,越來(lái)越?jīng)]存在感。

[[405737]]

本文轉(zhuǎn)載自微信公眾號(hào)「小姐姐味道」,作者小姐姐養(yǎng)的狗 。轉(zhuǎn)載本文請(qǐng)聯(lián)系小姐姐味道公眾號(hào)。

在Java的世界里,對(duì)象的存在層次,也有三六九等,充滿了階層之間的嘲弄。強(qiáng)軟弱虛各種引用,對(duì)于熟悉Java的同學(xué)一定不會(huì)感到陌生,它們隨著等級(jí)的降低,越來(lái)越?jīng)]存在感。平常使用的對(duì)象,大多數(shù)就是強(qiáng)引用的;而軟引用和弱引用,則經(jīng)常在一些堆內(nèi)緩存框架中用到。

那虛引用呢?傳說(shuō)中的幽靈引用,是不是就如同它的名字一樣,一無(wú)是處呢?

三種引用

首先,我們來(lái)回顧一下其他三種引用的類型和用途。

Strong references

當(dāng)內(nèi)存空間不足,系統(tǒng)撐不住了,JVM 就會(huì)拋出 OutOfMemoryError 錯(cuò)誤。即使程序會(huì)異常終止,這種對(duì)象也不會(huì)被回收。這種引用屬于最普通最強(qiáng)硬的一種存在,只有在和 GC Roots 斷絕關(guān)系時(shí),才會(huì)被消滅掉。

這種引用,你每天的編碼都在用。例如:new 一個(gè)普通的對(duì)象。

  1. Object obj = new Object() 

這種方式可能是有問(wèn)題的。假如你的系統(tǒng)被大量用戶(User)訪問(wèn),你需要記錄這個(gè) User 訪問(wèn)的時(shí)間??上У氖?,User 對(duì)象里并沒(méi)有這個(gè)字段,所以我們決定將這些信息額外開(kāi)辟一個(gè)空間進(jìn)行存放。

Soft references

軟引用用于維護(hù)一些可有可無(wú)的對(duì)象。在內(nèi)存足夠的時(shí)候,軟引用對(duì)象不會(huì)被回收,只有在內(nèi)存不足時(shí),系統(tǒng)則會(huì)回收軟引用對(duì)象,如果回收了軟引用對(duì)象之后仍然沒(méi)有足夠的內(nèi)存,才會(huì)拋出內(nèi)存溢出異常。

可以看到,這種特性非常適合用在緩存技術(shù)上。比如網(wǎng)頁(yè)緩存、圖片緩存等。

Guava 的 CacheBuilder,就提供了軟引用和弱引用的設(shè)置方式。在這種場(chǎng)景中,軟引用比強(qiáng)引用安全的多。

軟引用可以和一個(gè)引用隊(duì)列(ReferenceQueue)聯(lián)合使用,如果軟引用所引用的對(duì)象被垃圾回收,Java 虛擬機(jī)就會(huì)把這個(gè)軟引用加入到與之關(guān)聯(lián)的引用隊(duì)列中。

Weak references

弱引用對(duì)象相比較軟引用,要更加無(wú)用一些,它擁有更短的生命周期。

當(dāng) JVM 進(jìn)行垃圾回收時(shí),無(wú)論內(nèi)存是否充足,都會(huì)回收被弱引用關(guān)聯(lián)的對(duì)象。弱引用擁有更短的生命周期,在 Java 中,用 java.lang.ref.WeakReference 類來(lái)表示。

怪異的虛引用

以上幾個(gè)引用級(jí)別都很好理解,但是虛引用是個(gè)例外。虛引用可以使用下面的代碼定義:

  1. Object  object = new Object(); 
  2. ReferenceQueue queue = new ReferenceQueue(); 
  3. // 虛引用,必須與一個(gè)引用隊(duì)列關(guān)聯(lián) 
  4. PhantomReference pr = new PhantomReference(object, queue); 

但是當(dāng)你想取出其中的值時(shí)(get),得到的卻總是null。

  1. //JDK源碼    
  2. /** 
  3.      * Returns this reference object's referent.  Because the referent of a 
  4.      * phantom reference is always inaccessible, this method always returns 
  5.      * {@code null}. 
  6.      * 
  7.      * @return {@code null
  8.      */ 
  9.     public T get() { 
  10.         return null
  11.     } 

虛引用主要用來(lái)跟蹤對(duì)象被垃圾回收的活動(dòng)。

當(dāng)垃圾回收器準(zhǔn)備回收一個(gè)對(duì)象時(shí),如果發(fā)現(xiàn)它還有虛引用,就會(huì)在回收對(duì)象之前,把這個(gè)虛引用加入到與之關(guān)聯(lián)的引用隊(duì)列中。

程序如果發(fā)現(xiàn)某個(gè)虛引用已經(jīng)被加入到引用隊(duì)列,那么就可以在所引用的對(duì)象的內(nèi)存被回收之前采取必要的行動(dòng)。

桃花源深處

在hotspot的jvm中,有一個(gè)叫做cleaner的類,其實(shí)就是虛引用典型的應(yīng)用。可以看到Cleaner是直接簡(jiǎn)單粗暴的繼承了PhantomReference,所以它本質(zhì)上就是一個(gè)虛引用,只不過(guò)多了一些便捷的操作。

那么這個(gè)類是在什么地方用到的呢?大家手上應(yīng)該都有jdk的源代碼,追蹤一下,發(fā)現(xiàn)最后竟然是DirectByteBuffer用到了它。

直接內(nèi)存,一直是一個(gè)看起來(lái)非常高大上的名詞,基本上和高性能掛鉤,但也容易產(chǎn)生內(nèi)存泄漏。由于直接內(nèi)存,是屬于堆外內(nèi)存的,所以垃圾回收的時(shí)候,就不能靠JVM的那一套垃圾回收算法進(jìn)行清理。

事實(shí)上,由于DirectByteBuffer可能會(huì)被使用較長(zhǎng)時(shí)間,熬過(guò)了年輕代的各種回收,就會(huì)進(jìn)入老年代。這時(shí)候就比較麻煩了,這些引用對(duì)象,要在下一輪Old GC或者Full GC才能觸發(fā),如果你的老年代空間較大,觸發(fā)回收的操作就需要等很久很久。問(wèn)題是,在這段時(shí)間內(nèi),雖然這些堆外內(nèi)存不再使用了,但它仍然占用著較大的物理空間,最后造成嚴(yán)重的浪費(fèi)甚至崩潰。

對(duì)堆外內(nèi)存不是很熟悉的同學(xué),可以看我以前的一張圖?;蛘咧苯涌催@篇文章。通過(guò)-XX:MaxDirectMemorySize可以限制直接內(nèi)存的使用上限。

《一圖解千愁,jvm內(nèi)存從來(lái)沒(méi)有這么簡(jiǎn)單過(guò)!》

那么這些堆外內(nèi)存是如何進(jìn)行回收的呢?這就是Cleaner的作用。Cleaner通過(guò)next和prev構(gòu)造了一個(gè)典型的鏈表,但它本身是沒(méi)有任何邏輯的,因?yàn)樗那謇磉壿嫸荚趖hunk方法中。

  1. cleaner = Cleaner.create(this, new Deallocator(base, size, cap)); 
  2.  
  3. public void clean() { 
  4.         if (remove(this)) { 
  5.             try { 
  6.                 this.thunk.run(); 

也就是Deallocator = De allocator。其中,傳入的base,就是靠unsafe類申請(qǐng)的堆外內(nèi)存地址引用(僅僅是個(gè)地址),有了引用和容量,其實(shí)我們就能夠在回收的時(shí)候定位到真正的堆外內(nèi)存塊。就像Deallocator做的一樣。

  1. public void run() { 
  2.   if (address == 0) { 
  3.     // Paranoia 
  4.     return
  5.   } 
  6.   unsafe.freeMemory(address); 
  7.   address = 0; 
  8.   Bits.unreserveMemory(size, capacity); 

機(jī)制上沒(méi)什么問(wèn)題,關(guān)鍵要看它們是怎么聯(lián)系起來(lái)的。這種問(wèn)題,當(dāng)然是要靠其他線程完成,這里就是ReferenceHandler。很熟悉的名字,你每次使用jstack命令導(dǎo)出堆棧,都會(huì)看到它。

  1. Thread handler = new ReferenceHandler(tg, "Reference Handler"); 
  2. /* If there were a special system-only priority greater than 
  3. * MAX_PRIORITY, it would be used here 
  4. */ 
  5. handler.setPriority(Thread.MAX_PRIORITY); 
  6. handler.setDaemon(true); 
  7. handler.start(); 

真正去工作的方法,是tryHandlePending,然后在這里,調(diào)用Cleaner的clean方法,進(jìn)而調(diào)用真正的清理方法,釋放堆外內(nèi)存。它會(huì)從虛引用注冊(cè)的隊(duì)列里,取出新的對(duì)象,然后判斷是不是Cleaner類型,如果是,就進(jìn)行一次清理。

End

這就是虛引用。它存在的唯一目的,就是在回收的時(shí)候,能夠被感知到,以便進(jìn)行更深層次的清理。在commons-io包的FileCleaningTracker類中,同樣有繼承了虛引用的Tracker類,用來(lái)跟蹤后續(xù)文件的一些清理工作。這個(gè)沒(méi)存在感的小小虛引用,默默的承擔(dān)起最后一道防線,是系統(tǒng)正常運(yùn)行的有效保證。

不要小看它,它無(wú)處不在。因?yàn)槟愕拿恳粋€(gè)JVM進(jìn)程,都跑著一個(gè)叫做Reference Handler的線程呢。

作者簡(jiǎn)介:小姐姐味道 (xjjdog),一個(gè)不允許程序員走彎路的公眾號(hào)。聚焦基礎(chǔ)架構(gòu)和Linux。十年架構(gòu),日百億流量,與你探討高并發(fā)世界,給你不一樣的味道。我的個(gè)人微信xjjdog0,歡迎添加好友,進(jìn)一步交流。

 

責(zé)任編輯:武曉燕 來(lái)源: 小姐姐味道
相關(guān)推薦

2020-12-02 09:01:40

Java基礎(chǔ)

2009-06-19 16:19:23

Java對(duì)象引用

2012-05-09 13:36:30

WP7手機(jī)

2021-10-18 15:50:49

Android強(qiáng)引用軟引用

2013-02-21 14:20:47

2010-03-09 16:18:25

ArcGIS Expl

2022-01-17 10:18:33

騰訊末位淘汰制員工

2024-05-20 08:58:13

Java引用類型垃圾回收器

2009-10-09 16:25:00

CCNA考試個(gè)人心得CCNA

2020-02-26 21:57:09

Lambdajava8方法引用

2009-06-08 10:39:48

騰訊網(wǎng)友魔獸停服

2015-09-21 09:26:15

2010-01-18 17:38:54

C++虛函數(shù)表

2023-03-30 07:55:02

2017-04-24 18:39:24

人工智能

2011-05-25 09:49:35

項(xiàng)目

2011-11-04 16:44:45

iPhone應(yīng)用

2023-09-02 20:05:07

GNOME 45

2024-12-26 16:47:48

2010-05-21 10:56:59

Google SVN
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)