FinalReference 如何使 GC 過程變得拖拖拉拉

本文基于 OpenJDK17 進(jìn)行討論,垃圾回收器為 ZGC。

提示: 為了方便大家索引,特將在上篇文章 《以 ZGC 為例,談一談 JVM 是如何實(shí)現(xiàn) Reference 語義的》 中討論的眾多主題獨(dú)立出來。


FinalReference 對于我們來說是一種比較陌生的 Reference 類型,因?yàn)槲覀兒孟裨诟鞔笾虚g件以及 JDK 中并沒有見過它的應(yīng)用場景,事實(shí)上,F(xiàn)inalReference 被設(shè)計(jì)出來的目的也不是給我們用的,而是給 JVM 用的,它和 Java 對象的 finalize() 方法執(zhí)行機(jī)制有關(guān)。

public class Object {
    @Deprecated(since="9")
    protected void finalize() throws Throwable { }
}

我們看到 finalize() 方法在 OpenJDK9 中已經(jīng)被標(biāo)記為 @Deprecated 了,并不推薦使用。筆者其實(shí)一開始也并不想提及它,但是思來想去,本文是主要介紹各類 Refernce 語義實(shí)現(xiàn)的,前面筆者已經(jīng)非常詳細(xì)的介紹了 SoftReference,WeakReference,PhantomReference 在 JVM 中的實(shí)現(xiàn)。

在文章的最后何不利用這個 FinalReference 將前面介紹的內(nèi)容再次為大家串聯(lián)一遍,加深一下大家對 Reference 整個處理鏈路的理解,基于這個目的,才有了本小節(jié)的內(nèi)容。但筆者的本意并不是為了讓大家使用它。

下面我們還是按照老規(guī)矩,繼續(xù)從 JDK 以及 JVM 這兩個視角全方位的介紹一下 FinalReference 的實(shí)現(xiàn)機(jī)制,并為大家解釋一下這個 FinalReference 如何使整個 GC 過程變得拖拖拉拉,磨磨唧唧~~~

1. 從 JDK 視角看 FinalReference

image.png

FinalReference 本質(zhì)上來說它也是一個 Reference,所以它的基本語義和 WeakReference 保持一致,JVM 在 GC 階段對它的整體處理流程和 WeakReference 也是大致一樣的。

唯一一點(diǎn)不同的是,由于 FinalReference 是和被它引用的 referent 對象的 finalize() 執(zhí)行有關(guān),當(dāng)一個普通的 Java 對象在整個 JVM 堆中只有 FinalReference 引用它的時候,按照 WeakReference 的基礎(chǔ)語義來講,這個 Java 對象就要被回收了。

但是在這個 Java 對象被回收之前,JVM 需要保證它的 finalize()被執(zhí)行到,所以 FinalReference 會再次將這個 Java 對象重新標(biāo)記為 alive,也就是在 GC 階段重新復(fù)活這個 Java 對象。

后面的流程就和其他 Reference 一樣了,F(xiàn)inalReference 也會被 JVM 加入到 _reference_pending_list 鏈表中,ReferenceHandler 線程被喚醒,隨后將這個 FinalReference 從 _reference_pending_list 上摘下,并加入到與其關(guān)聯(lián)的 ReferenceQueue 中,這個流程就是我們第三小節(jié)主要討論的內(nèi)容,大家還記得嗎 ?

image.png

和 Cleaner 不同的是,對于 FinalReference 來說,在 JDK 中還有一個叫做 FinalizerThread 線程來專門處理它,FinalizerThread 線程會不斷的從與 FinalReference 關(guān)聯(lián)的 ReferenceQueue 中,將所有需要被處理的 FinalReference 摘下,然后挨個執(zhí)行被它所引用的 referent 對象的 finalize() 方法。

隨后在下一輪的 GC 中,F(xiàn)inalReference 對象以及它引用的 referent 對象才會被 GC 回收掉。

以上就是 FinalReference 被 JVM 處理的整個生命周期,下面讓我們先回到最初的起點(diǎn),這個 FinalReference 是怎么和一個 Java 對象關(guān)聯(lián)起來的呢 ?

我們知道 FinalReference 是和 Java 對象的 finalize() 方法執(zhí)行有關(guān)的,如果一個 Java 類沒有重寫 finalize() 方法,那么在創(chuàng)建這個 Java 類的實(shí)例對象的時候?qū)⒉粫瓦@個 FinalReference 有任何的瓜葛,它就是一個普通的 Java 對象。

但是如何一個 Java 類重寫了 finalize() 方法 ,那么在創(chuàng)建這個 Java 類的實(shí)例對象的時候, JVM 就會將一個 FinalReference 實(shí)例和這個 Java 對象關(guān)聯(lián)起來。

instanceOop InstanceKlass::allocate_instance(TRAPS) {
  // 判斷這個類是否重寫了 finalize() 方法
  bool has_finalizer_flag = has_finalizer(); 
  instanceOop i;
  // 創(chuàng)建實(shí)例對象
  i = (instanceOop)Universe::heap()->obj_allocate(this, size, CHECK_NULL);
  // 如果該對象重寫了  finalize() 方法
  if (has_finalizer_flag && !RegisterFinalizersAtInit) {
    // JVM 這里就會調(diào)用 Finalizer 類的靜態(tài)方法 register
    // 將這個 Java 對象與 FinalReference 關(guān)聯(lián)起來
    i = register_finalizer(i, CHECK_NULL);
  }
  return i;
}

我們看到,在 JVM 創(chuàng)建對象實(shí)例的時候,會首先通過 has_finalizer() 方法判斷這個 Java 類有沒有重寫 finalize() 方法,如果重寫了就會調(diào)用 register_finalizer 方法,JVM 最終會調(diào)用 JDK 中的 Finalizer 類的靜態(tài)方法 register。

final class Finalizer extends FinalReference<Object> {
    static void register(Object finalizee) {
        new Finalizer(finalizee);
    }
}

在這里 JVM 會將剛剛創(chuàng)建出來的普通 Java 對象 —— finalizee,與一個 Finalizer 對象關(guān)聯(lián)起來, Finalizer 對象的類型正是 FinalReference 。這里我們可以看到,當(dāng)一個 Java 類重寫了 finalize() 方法的時候,每當(dāng)創(chuàng)建一個該類的實(shí)例對象,JVM 就會自動創(chuàng)建一個對應(yīng)的 Finalizer 對象。

Finalizer 的整體設(shè)計(jì)和之前介紹的 Cleaner 非常相似,不同的是 Cleaner 是一個 PhantomReference,而 Finalizer 是一個 FinalReference。

它們都有一個 ReferenceQueue,只不過 Cleaner 中的那個基本沒啥用,但是 Finalizer 中的這個 ReferenceQueue 卻有非常重要的作用。

它們內(nèi)部都有一個雙向鏈表,里面包含了 JVM 堆中所有的 Finalizer 對象,用來確保這些 Finalizer 在執(zhí)行 finalizee 對象的 finalize() 方法之前不會被 GC 回收掉。

final class Finalizer extends FinalReference<Object> { 

    private static ReferenceQueue<Object> queue = new ReferenceQueue<>();

    // 雙向鏈表,保存 JVM 堆中所有的 Finalizer 對象,防止 Finalizer 被 GC 掉
    private static Finalizer unfinalized = null;

    private Finalizer next, prev;

    private Finalizer(Object finalizee) {
        super(finalizee, queue);
        // push onto unfinalized
        synchronized (lock) {
            if (unfinalized != null) {
                this.next = unfinalized;
                unfinalized.prev = this;
            }
            unfinalized = this;
        }
    }
}

在創(chuàng)建 Finalizer 對象的時候,首先會調(diào)用父類方法,將被引用的 Java 對象以及 ReferenceQueue 關(guān)聯(lián)注冊到 FinalReference 中。

    Reference(T referent, ReferenceQueue<? super T> queue) {
        // 被引用的普通 Java 對象
        this.referent = referent;
        //  Finalizer 中的 ReferenceQueue 實(shí)例(全局)
        this.queue = (queue == null) ? ReferenceQueue.NULL : queue;
    }

最后將這個 Finalizer 對象插入到雙向鏈表 —— unfinalized 中。

image.png

這個結(jié)構(gòu)是不是和第三小節(jié)中我們介紹的 Cleaner 非常相似。

image.png

而 Cleaner 最后是被 ReferenceHandler 線程執(zhí)行的,那這個 Finalizer 最后是被哪個線程執(zhí)行的呢 ?

這里就要引入另一個 system thread 了,在 Finalizer 類初始化的時候會創(chuàng)建一個叫做 FinalizerThread 的線程。

final class Finalizer extends FinalReference<Object> { 
    static {
        ThreadGroup tg = Thread.currentThread().getThreadGroup();
        // 獲取 system thread group
        for (ThreadGroup tgn = tg;
             tgn != null;
             tg = tgn, tgn = tg.getParent());
        // 創(chuàng)建 system thread : FinalizerThread
        Thread finalizer = new FinalizerThread(tg);
        finalizer.setPriority(Thread.MAX_PRIORITY - 2);
        finalizer.setDaemon(true);
        finalizer.start();
    }
}

FinalizerThread 的優(yōu)先級被設(shè)置為 Thread.MAX_PRIORITY - 2,還記得 ReferenceHandler 線程的優(yōu)先級嗎 ?

public abstract class Reference<T> {

    static {
        Thread handler = new ReferenceHandler(tg, "Reference Handler");
        // 設(shè)置 ReferenceHandler 線程的優(yōu)先級為最高優(yōu)先級
        handler.setPriority(Thread.MAX_PRIORITY);
        handler.setDaemon(true);
        handler.start();  
    }
}

而一個普通的 Java 線程,它的默認(rèn)優(yōu)先級是多少呢 ?

    /**
     * The default priority that is assigned to a thread.
     */
    public static final int NORM_PRIORITY = 5;

我們可以看出這三類線程的調(diào)度優(yōu)先級為:ReferenceHandler > FinalizerThread > Java 業(yè)務(wù) Thead

FinalizerThread 線程在運(yùn)行起來之后,會不停的從一個 queue 中獲取 Finalizer 對象,然后執(zhí)行 Finalizer 中的 runFinalizer 方法,這個邏輯是不是和 ReferenceHandler 線程不停的從 _reference_pending_list 中獲取 Cleaner 對象,然后執(zhí)行 Cleaner 的 clean 方法非常相似。

    private static class FinalizerThread extends Thread {

        public void run() {
            for (;;) {
                try {
                    Finalizer f = (Finalizer)queue.remove();
                    f.runFinalizer(jla);
                } catch (InterruptedException x) {
                    // ignore and continue
                }
            }
        }
    }

這個 queue 就是 Finalizer 中定義的 ReferenceQueue,在 JVM 創(chuàng)建 Finalizer 對象的時候,會將重寫了 finalize() 方法的 Java 對象與這個 ReferenceQueue 一起注冊到 FinalReference 中。

final class Finalizer extends FinalReference<Object> { 
    private static ReferenceQueue<Object> queue = new ReferenceQueue<>();
    private Finalizer(Object finalizee) {
        super(finalizee, queue);
    }
}

那這個 ReferenceQueue 中的 Finalizer 對象是從哪里添加進(jìn)來的呢 ?這就又和我們第三小節(jié)中介紹的內(nèi)容遙相呼應(yīng)起來了,就是 ReferenceHandler 線程添加進(jìn)來的。

private static class ReferenceHandler extends Thread {
    private static void processPendingReferences() {
        // ReferenceHandler 線程等待 JVM 向 _reference_pending_list 填充 Reference 對象
        waitForReferencePendingList();
        // 用于指向 JVM 的 _reference_pending_list
        Reference<?> pendingList;
        synchronized (processPendingLock) {
            // 獲取 _reference_pending_list,隨后將 _reference_pending_list 置為 null
            // 方便 JVM 在下一輪 GC 處理其他 Reference 對象
            pendingList = getAndClearReferencePendingList();
        }
        // 將 pendingList 中的 Reference 對象挨個從鏈表中摘下處理
        while (pendingList != null) {
            // 從 pendingList 中摘下 Reference 對象
            Reference<?> ref = pendingList;
            pendingList = ref.discovered;
            ref.discovered = null;
            
            // 如果該 Reference 對象是 Cleaner 類型,那么在這里就會調(diào)用它的 clean 方法
            if (ref instanceof Cleaner) {
                 // Cleaner 的 clean 方法就是在這里調(diào)用的
                ((Cleaner)ref).clean();
            } else {
                // 這里處理除 Cleaner 之外的其他 Reference 對象
                // 比如,其他 PhantomReference,WeakReference,SoftReference,F(xiàn)inalReference
                // 將他們添加到各自注冊的 ReferenceQueue 中
                ref.enqueueFromPending();
            }
        }
    }
}

當(dāng)一個 Java 對象在 JVM 堆中只有 Finalizer 對象引用,除此之外沒有任何強(qiáng)引用或者軟引用之后,JVM 首先會將這個 Java 對象復(fù)活,在本次 GC 中并不會回收它,隨后會將這個 Finalizer 對象插入到 JVM 內(nèi)部的 _reference_pending_list 中,然后從 waitForReferencePendingList() 方法上喚醒 ReferenceHandler 線程。

ReferenceHandler 線程將 _reference_pending_list 中的 Reference 對象挨個摘下,注意 _reference_pending_list 中保存的既有 Cleaner,也有其他的 PhantomReference,WeakReference,SoftReference,當(dāng)然也有本小節(jié)的 Finalizer 對象。

如果摘下的是 Cleaner 對象那么就執(zhí)行它的 clean 方法,如果是其他 Reference 對象,比如這里的 Finalizer,那么就通過 ref.enqueueFromPending(),將這個 Finalizer 對象插入到它的 ReferenceQueue 中。

當(dāng)這個 ReferenceQueue 有了 Finalizer 對象之后,F(xiàn)inalizerThread 線程就會被喚醒,然后執(zhí)行 Finalizer 對象的 runFinalizer 方法。

image.png

Finalizer 的內(nèi)部有一個雙向鏈表 —— unfinalized,它保存了當(dāng)前 JVM 堆中所有的 Finalizer 對象,目的是為了避免在執(zhí)行其引用的 referent 對象的 finalize()方法之前被 GC 掉。

在 runFinalizer 方法中首先要做的就是將這個 Finalizer 對象從雙向鏈表 unfinalized 上摘下,然后執(zhí)行 referent 對象的 finalize()方法。這里我們可以看到,大家在 Java 類中重寫的 finalize() 方法就是在這里被執(zhí)行的。

    private void runFinalizer(JavaLangAccess jla) {
        synchronized (lock) {
            if (this.next == this)      // already finalized
                return;
            // 將 Finalizer 對象從雙向鏈表 unfinalized 上摘下
            if (unfinalized == this)
                unfinalized = this.next;
            else
                this.prev.next = this.next;
            if (this.next != null)
                this.next.prev = this.prev;
            this.prev = null;
            this.next = this;           // mark as finalized
        }

        try {
            // 獲取 Finalizer 引用的 Java 對象
            Object finalizee = this.get();

            if (!(finalizee instanceof java.lang.Enum)) {
                // 執(zhí)行 java 對象的 finalize() 方法
                jla.invokeFinalize(finalizee);
            }
        } catch (Throwable x) { }
        // 調(diào)用 FinalReference 的 clear 方法,將其引用的 referent 對象置為 null
        // 下一輪 gc 的時候這個  FinalReference 以及它的 referent 對象就會被回收掉了。
        super.clear();
    }

最后調(diào)用 Finalizer 對象(FinalReference類型)的 clear 方法,將其引用的 referent 對象置為 null , 在下一輪 GC 的時候, 這個 Finalizer 對象以及它的 referent 對象就會被 GC 掉。

2. 從 JVM 視角看 FinalReference

現(xiàn)在我們已經(jīng)從 JVM 的外圍熟悉了 JDK 處理 FinalReference 的整個流程,本小節(jié),筆者將繼續(xù)帶著大家深入到 JVM 的內(nèi)部,看看在 GC 的時候,JVM 是如何處理 FinalReference 的。

在本文 5.1 小節(jié)中,筆者為大家介紹了 ZGC 在 Concurrent Mark 階段如何處理 Reference 的整個流程,只不過當(dāng)時我們偏重于 Reference 基礎(chǔ)語義的實(shí)現(xiàn),還未涉及到 FinalReference 的處理。

但我們在明白了 Reference 基礎(chǔ)語義的基礎(chǔ)之上,再來看 FinalReference 的語義實(shí)現(xiàn)就很簡單了,總體流程是一樣的,只不過在一些地方做了些特殊的處理。

image.png

在 ZGC 的 Concurrent Mark 階段,當(dāng) GC 線程遍歷標(biāo)記到一個 FinalReference 對象的時候,首先會通過 should_discover 方法來判斷是否應(yīng)該將這個 FinalReference 對象插入到 _discovered_list 中。判斷邏輯如下:

bool ZReferenceProcessor::should_discover(oop reference, ReferenceType type) const {
  // 獲取 referent 對象的地址視圖
  volatile oop* const referent_addr = reference_referent_addr(reference);
  // 調(diào)整 referent 對象的視圖為 remapped + mark0 也就是 weakgood 視圖
  // 獲取 FinalReference 引用的 referent 對象
  const oop referent = ZBarrier::weak_load_barrier_on_oop_field(referent_addr);

  // 如果 Reference 的狀態(tài)就是 inactive,那么這里將不會重復(fù)將 Reference 添加到 _discovered_list 重復(fù)處理
  if (is_inactive(reference, referent, type)) {
    return false;
  }
  // referent 還被強(qiáng)引用關(guān)聯(lián),那么 return false 也就是說不能被加入到 discover list 中
  if (is_strongly_live(referent)) {
    return false;
  }
  // referent 還被軟引用有效關(guān)聯(lián),那么 return false 也就是說不能被加入到 discover list 中
  if (is_softly_live(reference, type)) {
    return false;
  }

  return true;
}

首先獲取這個 FinalReference 對象所引用的 referent 對象,如果這個 referent 對象在 JVM 堆中已經(jīng)沒有任何強(qiáng)引用或者軟引用了,那么就會將 FinalReference 對象插入到 _discovered_list 中。

但是在插入之前還要通過 is_inactive 方法判斷一下這個 FinalReference 對象是否在上一輪 GC 中被處理過了,

bool ZReferenceProcessor::is_inactive(oop reference, oop referent, ReferenceType type) const {
  if (type == REF_FINAL) {
    return reference_next(reference) != NULL;
  } else {
    return referent == NULL;
  }
}

對于 FinalReference 來說,inactive 的標(biāo)志是它的 next 字段不為空。

public abstract class Reference<T> {
   volatile Reference next;
}

這里的 next 字段是干嘛的呢 ?比如說,這個 FinalReference 對象在上一輪的 GC 中已經(jīng)被處理過了,那么在發(fā)生本輪 GC 之前,ReferenceHandler 線程就已經(jīng)將這個 FinalReference 插入到一個 ReferenceQueue 中,這個 ReferenceQueue 是哪來的呢 ?

正是上小節(jié)中我們介紹的,JVM 創(chuàng)建 Finalizer 對象的時候傳入的這個 queue。

final class Finalizer extends FinalReference<Object> { 
    private static ReferenceQueue<Object> queue = new ReferenceQueue<>();
    private Finalizer(Object finalizee) {
        super(finalizee, queue);
    }
}

而 ReferenceQueue 中的 FinalReference 對象就是通過它的 next 字段鏈接起來的,當(dāng)一個 FinalReference 對象被 ReferenceHandler 線程插入到 ReferenceQueue 中之后,它的 next 字段就不為空了,也就是說一個 FinalReference 對象一旦進(jìn)入 ReferenceQueue,它的狀態(tài)就變?yōu)?inactive 了。

那么在下一輪的 GC 中如果一個 FinalReference 對象的狀態(tài)是 inactive,表示它已經(jīng)被處理過了,那么就不在重復(fù)添加到 _discovered_list 中了。

如果一個 FinalReference 對象之前沒有被處理過,并且它引用的 referent 對象當(dāng)前也沒有任何強(qiáng)引用或者軟引用關(guān)聯(lián),那么是不是說明這個 referent 就該被回收了 ?想想 FinalReference 的語義是什么 ? 是不是就是在 referent 對象被回收之前還要調(diào)用它的 finalize() 方法 。

所以為了保證 referent 對象的 finalize() 方法得到調(diào)用,JVM 就會在 discover 方法中將其復(fù)活。隨后會將 FinalReference 對象插入到 _discovered_list 中,這樣在 GC 之后 ,F(xiàn)inalizerThread 就會調(diào)用 referent 對象的 finalize() 方法了,這里是不是和上一小節(jié)的內(nèi)容呼應(yīng)起來了。

void ZReferenceProcessor::discover(oop reference, ReferenceType type) {
  // 復(fù)活 referent 對象
  if (type == REF_FINAL) {
    // 獲取 referent 地址視圖
    volatile oop* const referent_addr = reference_referent_addr(reference);
    // 如果是 FinalReference 那么就需要對 referent 進(jìn)行標(biāo)記,視圖改為 finalizable 表示只能通過 finalize 方法才能訪問到 referent 對象
    // 因?yàn)?referent 后續(xù)需要通過 finalize 方法被訪問,所以這里需要對它進(jìn)行標(biāo)記,不能回收
    ZBarrier::mark_barrier_on_oop_field(referent_addr, true /* finalizable */);
  }

  // Add reference to discovered list
  // 確保 reference 不在 _discovered_list 中,不能重復(fù)添加
  assert(reference_discovered(reference) == NULL, "Already discovered");
  oop* const list = _discovered_list.addr();
  // 頭插法,reference->discovered = *list
  reference_set_discovered(reference, *list);
  // reference 變?yōu)?_discovered_list 的頭部
  *list = reference;
}

那么 JVM 如何將一個被 FinalReference 引用的 referent 對象復(fù)活呢 ?

uintptr_t ZBarrier::mark_barrier_on_finalizable_oop_slow_path(uintptr_t addr) {
  // Mark,這里的 Finalizable = true
  return mark<GCThread, Follow, Finalizable, Overflow>(addr);
}
template <bool gc_thread, bool follow, bool finalizable, bool publish>
uintptr_t ZBarrier::mark(uintptr_t addr) {
  uintptr_t good_addr;

  // Mark,在 _livemap 標(biāo)記位圖中將 referent 對應(yīng)的 bit 位標(biāo)記為 1
  if (should_mark_through<finalizable>(addr)) {
    ZHeap::heap()->mark_object<gc_thread, follow, finalizable, publish>(good_addr);
  }

  if (finalizable) {
    // 調(diào)整 referent 對象的視圖為 finalizable
    return ZAddress::finalizable_good(good_addr);
  }

  return good_addr;
}

其實(shí)很簡單,首先通過 ZPage::mark_object 將 referent 對應(yīng)在標(biāo)記位圖 _livemap 的 bit 位標(biāo)記為 1。其次調(diào)整 referent 對象的地址視圖為 finalizable,表示該對象在回收階段被 FinalReference 復(fù)活。

inline bool ZPage::mark_object(uintptr_t addr, bool finalizable, bool& inc_live) {
  // Set mark bit, 獲取 referent 對象在標(biāo)記位圖的索引 index 
  const size_t index = ((ZAddress::offset(addr) - start()) >> object_alignment_shift()) * 2;
  // 將 referent 對應(yīng)的 bit 位標(biāo)記為 1
  return _livemap.set(index, finalizable, inc_live);
}

到現(xiàn)在 FinalReference 對象已經(jīng)被加入到 _discovered_list 中了,referent 對象也被復(fù)活了,隨后在 ZGC 的 Concurrent Process Non-Strong References 階段,JVM 就會將 _discovered_list 中的所有 Reference 對象(包括這里的 FinalReference)統(tǒng)統(tǒng)轉(zhuǎn)移到 _reference_pending_list 中,并喚醒 ReferenceHandler 線程去處理。

隨后 ReferenceHandler 線程將 _reference_pending_list 中的 FinalReference 對象在添加到 Finalizer 中的 ReferenceQueue 中。隨即 FinalizerThread 線程就會被喚醒,然后執(zhí)行 Finalizer 對象的 runFinalizer 方法,最終就會執(zhí)行到 referent 對象的 finalize() 方法。這是不是就和上一小節(jié)中的內(nèi)容串起來了。

image.png

當(dāng) referent 對象的 finalize() 方法被 FinalizerThread 執(zhí)行完之后,下一輪 GC 的這時候,這個 referent 對象以及與它關(guān)聯(lián)的 FinalReference 對象就會一起被 GC 回收了。

總結(jié)

從整個 JVM 對于 FinalReference 的處理過程可以看出,只要我們在一個 Java 類中重寫了 finalize() 方法,那么當(dāng)這個 Java 類對應(yīng)的實(shí)例可以被回收的時候,它的 finalize() 方法是一定會被調(diào)用的。

調(diào)用的時機(jī)取決于 FinalizerThread 線程什么時候被 OS 調(diào)度到,但是從另外一個側(cè)面也可以看出,由于 FinalReference 的影響,一個原本該被回收的對象,在 GC 的過程又會被 JVM 復(fù)活。而只有當(dāng)這個對象的 finalize() 方法被調(diào)用之后,該對象以及與它關(guān)聯(lián)的 FinalReference 只能等到下一輪 GC 的時候才能被回收。

如果 finalize() 方法執(zhí)行的很久又或者是 FinalizerThread 沒有被 OS 調(diào)度到,這中間可能已經(jīng)發(fā)生好幾輪 GC 了,那么在這幾輪 GC 中,F(xiàn)inalReference 和他的 referent 對象就一直不會被回收,表現(xiàn)的現(xiàn)象就是 JVM 堆中存在大量的 Finalizer 對象。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 1、引用級別 在JVM中,垃圾回收器一直在背后默默地承擔(dān)著內(nèi)存回收的工作,我們不需要像使用C語音開發(fā)那樣小心翼翼地...
    冰河winner閱讀 1,165評論 0 1
  • java引用體系中我們最熟悉的就是強(qiáng)引用類型,如 A a= new A();這是我們經(jīng)常說的強(qiáng)引用StrongRe...
    tracy_668閱讀 969評論 0 11
  • https://www.zhihu.com/question/62953438?from=profile_ques...
    Yves_Chen閱讀 212評論 0 0
  • JDK1.2之后,Java擴(kuò)充了引用的概念,將引用分為強(qiáng)引用、軟引用、弱引用和虛引用四種。 強(qiáng)引用類似于”O(jiān)bje...
    lesline閱讀 4,977評論 0 0
  • 感知GC。怎么感知:* 通過get來判斷已經(jīng)被GC(PhantomReference 在任何時候get都是null...
    YDDMAX_Y閱讀 1,979評論 0 4

友情鏈接更多精彩內(nèi)容