最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

java編程FinalReference與Finalizer原理示例詳解

 更新時間:2022年01月24日 10:13:08   作者:葉易_公眾號洞悉源碼  
這篇文章主要為大家介紹了java編程FinalReference與Finalizer的核心原理以及示例源碼的分析詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助

之前寫了一篇java編程Reference核心原理示例源碼分析的文章,但由于篇幅和時間的原因沒有給出FinalReference和Finalizer的分析。同時也沒有說明為什么建議不要重寫Object#finalize方法(實際上JDK9已經(jīng)將Object#finalize方法標(biāo)記為Deprecated)。將文章轉(zhuǎn)發(fā)到perfma社區(qū)后,社區(qū)便有同學(xué)提出一個有意思的問題?"Object#finalize如果在執(zhí)行的時候當(dāng)前對象又被重新賦值,那下次GC就不會再執(zhí)行finalize方法了,這是為什么啊” ??吹竭@個問題時我知道答案一定和Finalizer有關(guān),于是便有了這篇幅文章。(ps:perfma社區(qū)有很多高質(zhì)量的文章,同時里面有很多實用的工具JVM參數(shù)分析、Java線程dump分析、Java內(nèi)存dump分析都有,感興趣的同學(xué)可以關(guān)注一下。)

概述

java編程Reference核心原理示例源碼分析一文中提到JDK中有SoftReference、WeakReference、PhantomReference以及FinalReference,但并沒有細說FinalReference。最開始Java語言其實就有了finalizers的機制,然后才引用了特殊Reference機制,也就是SoftReference、WeakReference、PhantomReference以及FinalReference,通過他們來處理資源或內(nèi)存回收的問題。FinalReference與Finalizer平時開發(fā)時是用不到,但你Debug、線程dump或者heap dump 分析時,是否注意到Finalizer一直存在。

這個Finalizer到底是用來干什么的?為什么建議不要重寫Object#finalize方法?為什么如果在執(zhí)行Object#finalize方法時當(dāng)前對象又被重新賦值,那下次GC就不會再執(zhí)行finalize方法了?本文將通過源碼分析解釋這些問題。

初識FinalReference與Finalizer

JDK中FinalReference在JDK里的實現(xiàn)如下:

class FinalReference<T> extends Reference<T> {
    public FinalReference(T referent, ReferenceQueue<? super T> q) {
        super(referent, q);
    }
}

FinalReference實現(xiàn)很簡單,可以說就是一個標(biāo)記類,可以看到這個類訪問權(quán)限為package,除了java.lang.ref包下面的類能引用其外其他類都無權(quán)限。Finalizer實現(xiàn)則相對復(fù)雜一點點。

final class Finalizer extends FinalReference<Object> {
    //存放Finalizer的引用隊列
    private static ReferenceQueue<Object> queue = new ReferenceQueue<>();
    //當(dāng)前等待待執(zhí)行Object#finalize方法的Finalizer節(jié)點
    private static Finalizer unfinalized = null;
    //鎖對象
    private static final Object lock = new Object();
    //Finalizer鏈 后續(xù)節(jié)點與前驅(qū)節(jié)點
    private Finalizer next = null, prev = null;
    //私有構(gòu)造函數(shù)
    private Finalizer(Object finalizee) {
        super(finalizee, queue);
        //頭插法將當(dāng)前對象加入Finalizer鏈中
        add();
    }
    /* Invoked by VM */
    static void register(Object finalizee) {new Finalizer(finalizee);}
    //頭插法將當(dāng)前對象加入Finalizer鏈中
    private void add() {
        //獲取Finalizer類中全局鎖對象對應(yīng)moniter
        synchronized (lock) {
            if (unfinalized != null) {
                this.next = unfinalized;
                unfinalized.prev = this;
            }
            //更新等待待執(zhí)行Object#finalize方法的節(jié)點
            unfinalized = this;
        }
    }
}

從上面的JDK源碼代碼可以看到Finalizer對象實際是JVM通過調(diào)用Finalizer#register方法創(chuàng)建的,不通過反射我們是無法直接創(chuàng)建Finalizer對象的。Finalizer#register方法一方面創(chuàng)建了Finalizer對象,同時將創(chuàng)建的Finalizer對象加入到了Finalizer鏈中。實際上HotSpot實現(xiàn)上在創(chuàng)建一對象時,如果該類重寫了Object#finalize方法且方法內(nèi)容不為空,則會調(diào)Finalizer#register方法。

何時會調(diào)用類中重寫的finalize方法

先看回顧一下上篇文章中最重的Reference核心處理流程。通常JVM在GC時如果發(fā)現(xiàn)一個對象只有對應(yīng)的Reference引用就會將其對應(yīng)的Reference對象加入到對應(yīng)的pending-reference鏈中,同時會通知ReferenceHandler線程。ReferenceHandler線程收到通知后,如果對應(yīng)的Reference對象不是Cleaner的實例,則會其將加入到ReferenceQueue隊列中等待其他的線程去從ReferenceQueue中取出元素做進一步的清理工作。

同樣Reference核心處理流程也適用于Finalizer(Finalizer的超類實際是Reference),而用于處理ReferenceQueue中Finalizer的線程是FinalizerThread。其是Finalizer內(nèi)部的一個私有類,并且是一個守護線程。

private static class FinalizerThread extends Thread {
    private volatile boolean running;
    FinalizerThread(ThreadGroup g) {
        //這個便是一面提到dump線程時會出現(xiàn)的Finalizer線程的名字
        super(g, "Finalizer");
    }
    public void run() {
        // 避免重復(fù)調(diào)用run方法
        if (running)
            return;
        // Finalizer線程先于System.initializeSystemClass被調(diào)用。等待直到JavaLangAccess可以訪問
        while (!VM.isBooted()) {
            try {
                VM.awaitBooted();
            } catch (InterruptedException x) {
                // ignore and continue
            }
        }
        final JavaLangAccess jla = SharedSecrets.getJavaLangAccess();
        running = true;
        //守護線程一直運行
        for (;;) {
            try {
                //從ReferenceQueue中取出Finalizer
                Finalizer f = (Finalizer)queue.remove();
                //調(diào)用Finalizer引用對象重寫的finalize方法,內(nèi)部實現(xiàn)上會catch Throwable 異常,保證FinalizerThread線程一直能運行
                f.runFinalizer(jla);
            } catch (InterruptedException x) {
                // ignore and continue
            }
        }
    }
}
static {
    ThreadGroup tg = Thread.currentThread().getThreadGroup();
    for (ThreadGroup tgn = tg;
         tgn != null;
         tg = tgn, tgn = tg.getParent());     
    Thread finalizer = new FinalizerThread(tg);
    //線程優(yōu)先級沒有ReferenceHandler守護線程高
    finalizer.setPriority(Thread.MAX_PRIORITY - 2);
    //設(shè)置為守護線程
    finalizer.setDaemon(true);
    //啟動線程
    finalizer.start();
}

Finalizer#runFinalizer方法如下:

private void runFinalizer(JavaLangAccess jla) {
    synchronized (this) {
        //已從Finalizer鏈中摘除,則不再執(zhí)行Finalizer引用的對象的finalize方法
        if (hasBeenFinalized()) return;
        remove();
    }
    try {
        //獲取Finalizer引用的對象
        Object finalizee = this.get();
        if (finalizee != null && !(finalizee instanceof java.lang.Enum)) {
            /**JavaLangAccess實現(xiàn)內(nèi)部會調(diào)用Finalizer引用的對象的finalize方法
             * 實際是調(diào)用System#setJavaLangAccess方法實例化的JavaLangAccess對象
             */ 
            jla.invokeFinalize(finalizee);
            //清除棧中包含的該變量引用,以降低conservative GC 錯誤的保留該對象的機會
            finalizee = null;
        }
    } catch (Throwable x) { }
    super.clear();
}

問題答案

從上面Finalizer#runFinalizer方法源碼可以看出一旦一個對象已從Finalizer鏈中摘除,則不再執(zhí)行Finalizer引用的對象的finalize方法,即使在其finalize方法中再次強引用其本身。而另一個問題"為什么建議不要重寫Object#finalize方法",一旦重寫了finalize方法就無法保證其一定會在某次GC前一定能執(zhí)行完,這樣引用的對象只能在下次或者是后面GC時才會回收,這可能會出現(xiàn)內(nèi)存泄露或是其它的GC問題。關(guān)于finalize引發(fā)的GC問題,感興趣的同學(xué)可以看一下美團基礎(chǔ)構(gòu)架大佬寫的 RPC采用短鏈接導(dǎo)致YoungGC耗時過長的問題分析與優(yōu)化一文:一次 Young GC 的優(yōu)化實踐(FinalReference 相關(guān))

總結(jié)

本文分析了Finalizer的源碼,并給出了"為什么如果在執(zhí)行Object#finalize方法時當(dāng)前對象又被重新賦值,那下次GC就不會再執(zhí)行finalize方法了?"的答案。希望對大家有所幫忙。文章不正確處還望指正,同時歡迎關(guān)注個人技術(shù)公眾號 洞悉源碼,后序源源不斷地給大家分享各類干貨。最后再拋出一下問題給大家,JDK9中已將Object#finalize方法標(biāo)志為Deprecated,但如果我們要實現(xiàn)資源回收這種功能該如何實現(xiàn)呢?

相關(guān)文章

  • java如何從地址串中解析提取省市區(qū)(完美匹配中國所有地址)

    java如何從地址串中解析提取省市區(qū)(完美匹配中國所有地址)

    這篇文章主要給大家介紹了關(guān)于java如何從地址串中解析提取省市區(qū)的相關(guān)資料,通過這個方法可以完美匹配中國所有地址,文中通過示例代碼介紹的非常詳細,需要的朋友可以參考下
    2022-07-07
  • java操作elasticsearch詳細方法總結(jié)

    java操作elasticsearch詳細方法總結(jié)

    elasticsearch是使用Java編寫的一種開源搜索引擎,也是一種分布式的搜索引擎架構(gòu),這篇文章主要給大家介紹了關(guān)于java操作elasticsearch的相關(guān)資料,文中通過示例代碼介紹的非常詳細,需要的朋友可以參考下
    2022-12-12
  • java -length的三種用法說明

    java -length的三種用法說明

    這篇文章主要介紹了java -length的三種用法說明,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-09-09
  • 讀取xml文件中的配置參數(shù)實例

    讀取xml文件中的配置參數(shù)實例

    下面小編就為大家?guī)硪黄x取xml文件中的配置參數(shù)實例。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-10-10
  • Java實現(xiàn)導(dǎo)入csv的示例代碼

    Java實現(xiàn)導(dǎo)入csv的示例代碼

    這篇文章主要為大家詳細介紹了Java實現(xiàn)導(dǎo)入csv的相關(guān)知識,文中的示例代碼講解詳細,具有一定的借鑒價值,有需要的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2024-03-03
  • 淺談Java 中的引用類型

    淺談Java 中的引用類型

    Java開發(fā)者肯定都很熟悉java中的4種引用類型,它們從強到弱分別是:強引用、軟引用、弱引用和虛引用,下面我們詳細看看這些引用類型
    2020-02-02
  • IDEA插件之mybatisx?插件使用教程

    IDEA插件之mybatisx?插件使用教程

    這篇文章主要介紹了mybatisx?插件使用教程,包括插件安裝自動生成代碼的相關(guān)知識,本文通過實例圖文相結(jié)合給大家介紹的非常詳細,需要的朋友可以參考下
    2022-05-05
  • Java獲取任意http網(wǎng)頁源代碼的方法

    Java獲取任意http網(wǎng)頁源代碼的方法

    這篇文章主要介紹了Java獲取任意http網(wǎng)頁源代碼的方法,可實現(xiàn)獲取網(wǎng)頁代碼以及去除HTML標(biāo)簽的代碼功能,涉及Java正則操作相關(guān)實現(xiàn)技巧,需要的朋友可以參考下
    2017-09-09
  • Java 異常詳解

    Java 異常詳解

    本文主要介紹了異常與錯誤的區(qū)別,異常的體現(xiàn)分類,異常的處理機制,如何自定義異常等,具有很好的參考價值,下面跟著小編一起來看下吧
    2017-02-02
  • 分布式服務(wù)Dubbo+Zookeeper安全認證實例

    分布式服務(wù)Dubbo+Zookeeper安全認證實例

    下面小編就為大家分享一篇分布式服務(wù)Dubbo+Zookeeper安全認證實例,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2017-12-12

最新評論

镇宁| 南和县| 申扎县| 鹰潭市| 孝昌县| 弋阳县| 太康县| 麻江县| 普格县| 偃师市| 宿州市| 仁布县| 吴川市| 赤峰市| 那坡县| 博客| 湛江市| 烟台市| 迁西县| 札达县| 红河县| 广灵县| 松溪县| 璧山县| 潞西市| 原平市| 卫辉市| 辛集市| 庄河市| 隆昌县| 太仆寺旗| 财经| 山阳县| 镇远县| 北宁市| 广东省| 睢宁县| 克山县| 富锦市| 灵丘县| 大同市|