日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Reference和ReferenceQueue

發(fā)布時間:2025/3/15 编程问答 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Reference和ReferenceQueue 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

我們都知道在堆里面存放著Java中幾乎所有的對象實例,垃圾收集器在對堆進行回收前,第一件事情就是要確定這些對象之中哪些還“存活”著,哪些已經“死去”。那么gc怎么判斷一個對象是不是垃圾呢

判斷對象是否存活有兩種計數算法:引用計數法、可達性分析法

引用計數法:在對象中添加一個引用計數器,每當有一個地方引用它時,計數器值就加一;當引用失效時,計數器值就減一 就是如果一個對象沒有被任何引用指向,則可視之為垃圾。

可達性分析法:通過一系列的稱為GC Roots的對象作為起始點,從這些節(jié)點開始向下搜索,搜索所走過的路徑稱為引用鏈Reference Chain,當一個對象到GC Root沒有任何引用鏈相連時,則證明此對象是不可用的

這兩種方式我們不做過多的介紹。但我們可以發(fā)現無論是通過引用計數算法判斷對象的引用數量,還是通過可達性分析算法判斷對象是否引用鏈可達,判定對象是否存活都和引用離不開關系。

下來我們就開始說說引用

一、引用

要了解Reference和ReferenceQueue,我們需要先知道什么是引用。

我們用圖來展示一下 Java中new一個對象 在內存中的創(chuàng)建過程

我們可以看出創(chuàng)建一個對象并用一個引用指向它的過程:

  • 在堆內存中創(chuàng)建一個Student類的對象(new Student())

  • 在棧內存中聲明一個指向Student類型對象的變量(Student obj)

  • 將棧內存中的引用變量obj指向堆內存中的對象new Student()。

  • 這樣把一個對象賦給一個變量,這個變量obj就是引用

    在JDK1.2版之前,Java里面的引用是很傳統(tǒng)的定義:

    如果reference類型的數據中存儲的數值代表的是另外一塊內存的起始地址,就稱該reference數據是代表某塊內存、某個對象的引用。

    在JDK 1.2版之后,Java對引用的概念進行了擴充,將引用分為4種:

    • 強引用(Strong Reference)
    • 軟引用(Soft Reference)
    • 弱引用(Weak Reference)
    • 虛引用(Phantom Reference)

    Java 中引入四種引用的目的就是讓程序自己決定對象的生命周期。JVM通過垃圾回收器對這四種引用做不同的處理,來實現對象生命周期的改變。關于這4種引用的介紹這里不詳細展開,本文主要介紹Reference和ReferenceQueue

    二、ReferenceQueue

    對于軟引用、弱引用和虛引用,都可以和一個引用隊列ReferenceQueue 聯(lián)合使用,如果軟/弱/虛引用中的對象被回收,那么軟/弱/虛引用就會被 JVM加入關聯(lián)的引用隊列ReferenceQueue中。 也就是說我們可以通過監(jiān)控引用隊列來判斷Reference引用的對象是否被回收,從而執(zhí)行相應的方法。
    例如下面的例子. 如果弱引用中的對象(obj)被回收,那么軟引用weakRef就會被 JVM 加入到引用隊列queue 中。 這樣當我們想檢測obj對象是否被回收了 ,就可以從 queue中讀取相應的 Reference 來判斷obj是否被回收,從而執(zhí)行相應的方法。

    ReferenceQueue<Object> queue = new ReferenceQueue<>(); Object obj = new Object(); WeakReference weakRef = new WeakReference<Object>(obj,queue);//置空 obj = null; System.out.println("gc之后的值: " + weakRef.get()); // 對象依然存在//調用gc System.gc();//如果obj被回收,則軟引用會進入引用隊列 Reference<?> reference = queue.remove(); if (reference != null){System.out.println("對象已被回收: "+ reference.get()); // 對象為null }

    三、Reference源碼(JDK8)

    要理解Reference 源碼要從幾個方面來看:

  • Reference 對象的4種狀態(tài)是如何轉換
  • pending-Reference list的賦值和作用
  • Reference-handler的作用
  • ReferenceQueue的作用
  • 我們打開Reference的源碼,可以看到最開始有一段注釋說明,介紹了引用的四種狀態(tài)Active ,Pending ,Enqueued ,Inactive

    翻譯過來大意如下:

  • Active
    新創(chuàng)建的Reference實例的狀態(tài)是Active。GC檢測到Reference引用的實際對象的可達性發(fā)生某些改變后,它的狀態(tài)將變化為Pending或Inactive。Reference注冊了ReferenceQueue,則會切換為Pending,并且Reference會加入pending-Reference list中。
    Reference沒有注冊ReferenceQueue,會切換為Inactive。
  • Pending
    在pending-Reference list中等著被Reference-handler 入隊列queue中的元素就處于這個狀態(tài)。沒有注冊ReferenceQueue的實例是永遠不可能到達這一狀態(tài)。
  • Enqueued
    Enqueued:在ReferenceQueue隊列中時Reference的狀態(tài),如果Reference從隊列中移除,會進入Inactive狀態(tài)
  • Inactive
    一旦一個實例變?yōu)镮nactive,則這個狀態(tài)永遠都不會再被改變。
    它們的關系下圖很清晰的表示了出來
  • Reference源碼中并不存在一個成員變量用于描述Reference的狀態(tài),它是通過他的成員變量的存在性"拼湊出"對應的狀態(tài)。

    然后我們看下Reference內部的成員變量

    public abstract class Reference<T> {private T referent; volatile ReferenceQueue<? super T> queue;Reference next;transient private Reference<T> discovered;static private class Lock { }private static Lock lock = new Lock();private static Reference<Object> pending = null; }
    • referent:指reference引用的對象

    • queue:引用隊列,Reference引用的對象被回收時,Reference實例會被放入引用隊列,我們可以從ReferenceQueue得到Reference實例,執(zhí)行我們自己的操作

    • next:下一個Reference實例的引用,Reference實例通過此構造單向的鏈表。ReferenceQueue并不是一個鏈表數據結構,它只持有這個鏈表的表頭對象header,這個鏈表就是由next構建起來的,next也就是鏈表當前節(jié)點的下一個節(jié)點

    • pending:等待加入隊列的引用列表,GC檢測到某個引用實例指向的實際對象不可達后,會將該pending指向該引用實例。pending與discovered一起構成了一個pending單向鏈表,pending為鏈表的頭節(jié)點,discovered為鏈表當前Reference節(jié)點指向下一個節(jié)點的引用,這個隊列是由jvm的垃圾回收器構建的,當對象除了被reference引用之外沒有其它強引用了,jvm的垃圾回收器就會將指向需要回收的對象的Reference都放入到這個隊列里面。這個隊列會由ReferenceHander線程來處理,它的任務就是將pending隊列中要被回收的Reference對象移除出來,

    • discovered:pending list中下一個需要被處理的實例,在處理完當前pending之后,將discovered指向的實例賦予給pending即可。所以這個pending就相當于是一個鏈表。

    我們來看一個弱引用的回收過程,來了解他的成員變量和四種狀態(tài)的轉換

    ReferenceQueue<Object> queue = new ReferenceQueue<>();WeakReference mWreference = new WeakReference(new Object(), queue); System.gc();Reference mReference = queue.remove();
  • 創(chuàng)建弱引用,此時狀態(tài)為Active,pending= null,discovered = null
  • 執(zhí)行GC,由于是弱引用,所以回收該object對象,將引用mWreference 放入pending隊列,等待被ReferenceHandler線程處理.此時狀態(tài)為PENDING,pending=mWreference,discovered
    = pending-Reference列表中的下一個元素
  • ReferenceHandler從pending隊列中取下mWreference,并且將mWreference放入到queue中,此時Reference狀態(tài)為Enqueued,調用了ReferenceQueue.enqueued()后的Reference實例就會處于這個狀態(tài)
  • 當從queue里面取出該元素,則變?yōu)镮NACTIVE
  • 四、ReferenceHandler

    從上面的分析我們知道ReferenceHandle線程的主要功能就是把pending list中的引用實例添加到引用隊列ReferenceQueue中,并將pending指向下一個引用實例。

    ReferenceHandler是Reference類的一個內部類,由Reference靜態(tài)代碼塊中建立并且運行的線程,只要Reference這個父類被初始化,該線程就會創(chuàng)建和運行,由于它是守護線程,除非JVM進程終結,否則它會一直在后臺運行 。

    private static class ReferenceHandler extends Thread {...public void run() {while (true) {tryHandlePending(true);}}...}static boolean tryHandlePending(boolean waitForNotify) {...synchronized (lock) {//如果pending隊列不為空,則將第一個Reference對象取出if (pending != null) {//緩存pending隊列頭節(jié)點r = pending;// 'instanceof' might throw OutOfMemoryError sometimes// so do this before un-linking 'r' from the 'pending' chain...c = r instanceof Cleaner ? (Cleaner) r : null;// unlink 'r' from 'pending' chain//將頭節(jié)點指向discovered,discovered為pending隊列中當前節(jié)點的下一個節(jié)點,這樣就把第一個頭結點出隊了pending = r.discovered;//將當前節(jié)點的discovered設置為null;當前節(jié)點出隊,不需要組成鏈表了;r.discovered = null;} else {// The waiting on the lock may cause an OutOfMemoryError// because it may try to allocate exception objects.if (waitForNotify) {lock.wait();}// retry if waitedreturn waitForNotify;}}//將對象放入到它自己的ReferenceQueue隊列里ReferenceQueue<? super Object> q = r.queue;if (q != ReferenceQueue.NULL) q.enqueue(r);return true;}

    五、ReferenceQueue

    • ReferenceQueue:在Reference引用的對象被回收時,Reference對象進入到pending隊列, 由ReferenceHander線程處理后,Reference就被放到ReferenceQueue里面,然后我們就可以從ReferenceQueue里拿到reference,執(zhí)行我們自己的操作。這樣我們只需要 ReferenceQueue就可以知道Reference持有的對象是否被回收。

    • 如果不帶ReferenceQueue的話,要想知道Reference持有的對象是否被回收,就只有不斷地輪訓reference對象,通過判斷里面的get是否為null(phantomReference對象不能這樣做,其get始終返回null,因此它只有帶queue的構造函數)。

    • 這兩種方法均有相應的使用場景。如weakHashMap中就選擇去查詢queue的數據,來判定是否有對象將被回收.而ThreadLocalMap,則采用判斷get()是否為null來作處理;

    • ReferenceQueue只存儲了Reference鏈表的頭節(jié)點,真正的Reference鏈表的所有節(jié)點是存儲在Reference實例本身,Reference通過成員屬性next構建單向鏈表,ReferenceQueue提供了對Reference鏈表的入隊、poll、remove等操作

    public class ReferenceQueue<T> {boolean enqueue(Reference<? extends T> r) { synchronized (lock) {// 如果引用實例持有的隊列為ReferenceQueue.NULL或者ReferenceQueue.ENQUEUED則入隊失敗返回falseReferenceQueue<?> queue = r.queue;if ((queue == NULL) || (queue == ENQUEUED)) {return false;}assert queue == this;// 當前引用實例已經入隊,那么它本身持有的引用隊列實例置為ReferenceQueue.ENQUEUEDr.queue = ENQUEUED;// 接著,將 Reference 插入到鏈表// 如果鏈表沒有元素,則此引用實例直接作為頭節(jié)點,否則把前一個引用實例作為下一個節(jié)點r.next = (head == null) ? r : head;// 當前實例更新為頭節(jié)點,也就是每一個新入隊的引用實例都是作為頭節(jié)點,已有的引用實例會作為后繼節(jié)點head = r;// 隊列長度增加1queueLength++;if (r instanceof FinalReference) {sun.misc.VM.addFinalRefCount(1);}lock.notifyAll();return true;}}// 引用隊列的poll操作,此方法必須在加鎖情況下調用private Reference<? extends T> reallyPoll() { /* Must hold lock */Reference<? extends T> r = head;if (r != null) {r.queue = NULL;// Update r.queue *before* removing from list, to avoid// race with concurrent enqueued checks and fast-path// poll(). Volatiles ensure ordering.@SuppressWarnings("unchecked")Reference<? extends T> rn = r.next;// Handle self-looped next as end of list designator.// 更新next節(jié)點為頭節(jié)點,如果next節(jié)點為自身,說明已經走過一次出隊,則返回nullhead = (rn == r) ? null : rn;// Self-loop next rather than setting to null, so if a// FinalReference it remains inactive.// 當前頭節(jié)點變更為環(huán)狀隊列,考慮到FinalReference尚為inactive和避免重復出隊的問題r.next = r;// 隊列長度減少1queueLength--;// 特殊處理FinalReference,VM進行計數if (r instanceof FinalReference) {VM.addFinalRefCount(-1);}return r;}return null;}// 隊列的公有poll操作,主要是加鎖后調用reallyPollpublic Reference<? extends T> poll() {if (head == null)return null;synchronized (lock) {return reallyPoll();}} }

    文章轉自

    總結

    以上是生活随笔為你收集整理的Reference和ReferenceQueue的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

    主站蜘蛛池模板: 国产激情91 | 久久精品人人爽 | 99精品欧美一区二区蜜桃免费 | 精品伦精品一区二区三区视频密桃 | 九九久久九九久久 | 日韩一级中文字幕 | 久久国产精品免费观看 | 五月婷婷六月激情 | ass精品国模裸体欣赏pics | 超碰个人在线 | 91精品国产综合久久国产大片 | 视频一区免费 | 国产精品久久777777毛茸茸 | 亚洲无码久久久久久久 | 最近中文字幕一区二区 | 日本黄色不卡 | 97激情| 国产精品久久久免费观看 | 欧美熟妇另类久久久久久多毛 | 国产成人在线播放视频 | 97人妻精品一区二区三区免费 | 女人扒开腿让男人桶爽 | 欧美激情91 | 日本一区视频在线观看 | 中文字幕一区二区三区不卡 | 国产美女免费网站 | 理论片午午伦夜理片影院99 | 少妇粉嫩小泬白浆流出 | 成人国产精品一区二区 | 免费看黄色aaaaaa 片 | 欧美精品电影一区二区 | 国产精品手机在线 | 亚洲人成影视 | 中文视频一区二区 | 久久99久久精品 | 一区二区视频在线看 | 亚洲精品国产精品乱码不99按摩 | 久草视频观看 | 国产真实乱在线更新 | 99久久久无码国产 | 亚洲一区影院 | 诱夫1v1高h | mm视频在线观看 | 91插插插插插 | 国产精品揄拍一区二区 | 无码人妻精品一区二区蜜桃网站 | 国产伦精品一区二区 | h在线免费观看 | 精品国产18久久久久久二百 | 好男人.www| 在线成人看片 | 国产精品网页 | 美女在线免费视频 | 久久网站视频 | 国产97在线视频 | 久久久人妻无码一区二区 | 国产亚洲系列 | 国产福利91精品 | 欧美一级片 | 男人懂的网站 | 精品在线播放视频 | 欧美日p视频 | 国产精品久久久久久久无码 | 久久性精品 | 淫岳高潮记小说 | 碧蓝之海动漫在线观看免费高清 | 91网站在线免费看 | 蜜桃视频污 | 女人做爰全过程免费观看美女 | 一区二区成人在线 | 开心激情五月网 | 国产一级做a爱片久久毛片a | 久久久永久久久人妻精品麻豆 | 成人免费公开视频 | 久久国产精品区 | 99re免费视频| 欧美成人二区 | 国产亚洲精品久久久久久久久动漫 | 国产美女av在线 | 宅男在线视频 | 日批在线观看视频 | 大吊av | 欧美 日韩 高清 | 欧美变态视频 | 国产一区二区在线免费观看视频 | 99福利在线 | 西欧毛片 | 亚洲视频在线观看一区二区 | 久久黄色网络 | 亚洲精品一区二区在线 | 天天干,夜夜操 | 中文字幕乱码中文乱码b站 国产一区二区三区在线观看视频 | av色区 | 精品人妻一区二区色欲产成人 | 国产一区二区三区免费看 | 91免费黄色 | 日韩电影一二三区 | 91网站在线免费观看 | 激情视频国产 |