01 PhantomReference沒有進入ReferenceQueue

前言 

最近 看到一篇文章, 然後 基於改文章的代碼, 做了一些 調整, 然後 發現了一些 奇怪的事情, 並稍微整理了一下 原因

該文章地址 : https://hllvm-group.iteye.com/group/topic/34934

 

測試代碼如下 : 

package com.hx.test02;

import java.lang.ref.PhantomReference;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.reflect.Field;

/**
 * Test02WeakReferenceAfterGc
 *
 * @author Jerry.X.He <[email protected]>
 * @version 1.0
 * @date 2019-10-14 11:23
 */
public class Test02WeakReferenceAfterGc {

  // Test02WeakReferenceAfterGc
  // refer : https://hllvm-group.iteye.com/group/topic/34934
  public static void main(String[] args) throws Exception {
    ReferenceQueue<Obj> referenceQueue = new ReferenceQueue<>();
    for (int i = 0; i < 1; i++) {
      Obj o = new Obj("object_" + i);
//      SoftReference<Obj> ref = new SoftReference<>(o, referenceQueue);
//      WeakReference<Obj> ref = new WeakReference<>(o, referenceQueue);
      PhantomReference<Obj> ref = new PhantomReference<>(o, referenceQueue);
      o = null;
      System.gc();
      Field field = Reference.class.getDeclaredField("referent");
      field.setAccessible(true);
      System.out.println(field.get(ref));
    }

    // 這個現象, 有 finalize 和 沒有 finalize 是兩個不同的情況, 按照理論上來說[常規的思考], 有 finalize 的情況下, 應該會有兩個 Reference 分別進入 兩個隊列, 但是沒有
    Thread.sleep(3000);
    System.gc();

    // consumer
    new Thread() {
      public void run() {
        while (true) {
          Object o = referenceQueue.poll();
          if (o != null) {
            try {
              Field rereferent = Reference.class.getDeclaredField("referent");
              rereferent.setAccessible(true);
              Object result = rereferent.get(o);
              System.out.println("gc will collect : " + o.getClass() + "@" + o.hashCode() + ", referent : " + result);
            } catch (Exception e) {
              e.printStackTrace();
            }
          }
        }
      }
    }.start();

  }

  /**
   * Obj
   *
   * @author Jerry.X.He <[email protected]>
   * @version 1.0
   * @date 2019-10-14 11:24
   */
  static class Obj {
    private final String name;

    Obj(String name) {
      this.name=name;
    }

    // test for FinalReference
//    @Override
//    protected void finalize() throws Throwable {
//      System.out.println("執行finalize方法:" + name);
//      super.finalize();
//    }

    @Override
    public String toString() {
      return name;
    }
  }


}

 

然後 我們這裏 需要關注的主要的問題在於, Test02WeakReferenceAfterGc$Obj 在沒有重寫 finalize 方法的時候, 輸出如下 

object_0
gc will collect : class java.lang.ref.PhantomReference@1964850888, referent : object_0

在 Test02WeakReferenceAfterGc$Obj 重寫了 finalize 方法之後 輸出如下 

object_0
執行finalize方法:object_0

 

這裏 直觀感覺就很奇怪了呀, 爲什麼 是否重寫 finalize 方法, 會影響到 PhantomReference 進入對應的 引用隊列呢 ? 

 

針對重寫 finalize 方法的情況討論

最開始 我以爲是 第一個 System.gc 之後,  Obj 的實例 o 對應的 FinalReferece 會引用該對象, 所以該 對象在gc的時候沒有被清理, 所以 該 PhantomReference 沒有被整理, 然後 上面的這段代碼, 是我爲了這個猜想增加的 

        Thread.sleep(3000);
        System.gc();

但是, 發現 給定的 PhantomReference 還是沒有  進入對應的引用隊列 

這個就很令人 疑惑了 

 

測試代碼均在jdk8下面編譯, 以上測試代碼結果, 運行於 jdk8, 一下部分代碼, 截圖基於 openjdk9 

在 openjdk9 下面運行該測試方法, ref.get() 均爲 null, 後面會提及到這一點的原因 

以下描述 僅僅會描述一些和我們這裏相關的部分, 整體的其他邏輯不會做過多描述 

以下調試的參數爲 

-da -dsa -Xint -XX:+UseSerialGC com.hx.test02.Test02WeakReferenceAfterGc

以下調試的基於 openjdk9, 並且有一部分個人方便調試的調整, 調整的部分爲 

jvm.cpp 

JVM_ENTRY_NO_ENV(void, JVM_GC(void))
  JVMWrapper("JVM_GC");
  if (!DisableExplicitGC) {
-    Universe::heap()->collect(GCCause::_java_lang_system_gc);
+    VM_GenCollectForAllocation op(100, true, Universe::heap()->total_collections());
+    VMThread::execute(&op);
  }
JVM_END

System.gc 調整成爲 ygc, 所以 可能和我們直接使用 jdk 運行的運行時情況稍有不一致的地方, 所以本文的調試 可能會存在一些侷限 

另外 由於本人水平有限, 理解能力有限有限, 可能也會導致一些問題的存在 

 

問題的細節

1. 相關代碼片段

首先先貼一下一部分關鍵代碼, 在gc處理的過程中 會進行引用處理, 這裏的 _discoveredXXRefs 來自於 複製算法遍歷活躍對象的時候額外的針對 Reference 對象做了一些 Discover[參見instanceRefKlass.inline.oop_oop_iterate]

ReferenceProcessor::process_discovered_references

ReferenceProcessorStats ReferenceProcessor::process_discovered_references(
  BoolObjectClosure*           is_alive,
  OopClosure*                  keep_alive,
  VoidClosure*                 complete_gc,
  AbstractRefProcTaskExecutor* task_executor,
  GCTimer*                     gc_timer) {

  assert(!enqueuing_is_done(), "If here enqueuing should not be complete");
  // Stop treating discovered references specially.
  disable_discovery();

  // If discovery was concurrent, someone could have modified
  // the value of the static field in the j.l.r.SoftReference
  // class that holds the soft reference timestamp clock using
  // reflection or Unsafe between when discovery was enabled and
  // now. Unconditionally update the static field in ReferenceProcessor
  // here so that we use the new value during processing of the
  // discovered soft refs.

  _soft_ref_timestamp_clock = java_lang_ref_SoftReference::clock();

  ReferenceProcessorStats stats(
      total_count(_discoveredSoftRefs),
      total_count(_discoveredWeakRefs),
      total_count(_discoveredFinalRefs),
      total_count(_discoveredPhantomRefs));

  // Soft references
  {
    GCTraceTime(Debug, gc, ref) tt("SoftReference", gc_timer);
    process_discovered_reflist(_discoveredSoftRefs, _current_soft_ref_policy, true,
                               is_alive, keep_alive, complete_gc, task_executor);
  }

  update_soft_ref_master_clock();

  // Weak references
  {
    GCTraceTime(Debug, gc, ref) tt("WeakReference", gc_timer);
    process_discovered_reflist(_discoveredWeakRefs, NULL, true,
                               is_alive, keep_alive, complete_gc, task_executor);
  }

  // Final references
  {
    GCTraceTime(Debug, gc, ref) tt("FinalReference", gc_timer);
    process_discovered_reflist(_discoveredFinalRefs, NULL, false,
                               is_alive, keep_alive, complete_gc, task_executor);
  }

  // Phantom references
  {
    GCTraceTime(Debug, gc, ref) tt("PhantomReference", gc_timer);
    process_discovered_reflist(_discoveredPhantomRefs, NULL, true,
                               is_alive, keep_alive, complete_gc, task_executor);
  }

  // Weak global JNI references. It would make more sense (semantically) to
  // traverse these simultaneously with the regular weak references above, but
  // that is not how the JDK1.2 specification is. See #4126360. Native code can
  // thus use JNI weak references to circumvent the phantom references and
  // resurrect a "post-mortem" object.
  {
    GCTraceTime(Debug, gc, ref) tt("JNI Weak Reference", gc_timer);
    if (task_executor != NULL) {
      task_executor->set_single_threaded_mode();
    }
    process_phaseJNI(is_alive, keep_alive, complete_gc);
  }

  log_debug(gc, ref)("Ref Counts: Soft: " SIZE_FORMAT " Weak: " SIZE_FORMAT " Final: " SIZE_FORMAT " Phantom: " SIZE_FORMAT,
                     stats.soft_count(), stats.weak_count(), stats.final_count(), stats.phantom_count());
  log_develop_trace(gc, ref)("JNI Weak Reference count: " SIZE_FORMAT, count_jni_refs());

  return stats;
}

 

ReferenceProcessor::process_discovered_reflist

void ReferenceProcessor::process_discovered_reflist(
  DiscoveredList               refs_lists[],
  ReferencePolicy*             policy,
  bool                         clear_referent,
  BoolObjectClosure*           is_alive,
  OopClosure*                  keep_alive,
  VoidClosure*                 complete_gc,
  AbstractRefProcTaskExecutor* task_executor)
{
  bool mt_processing = task_executor != NULL && _processing_is_mt;
  // If discovery used MT and a dynamic number of GC threads, then
  // the queues must be balanced for correctness if fewer than the
  // maximum number of queues were used.  The number of queue used
  // during discovery may be different than the number to be used
  // for processing so don't depend of _num_q < _max_num_q as part
  // of the test.
  bool must_balance = _discovery_is_mt;

  if ((mt_processing && ParallelRefProcBalancingEnabled) ||
      must_balance) {
    balance_queues(refs_lists);
  }

  // Phase 1 (soft refs only):
  // . Traverse the list and remove any SoftReferences whose
  //   referents are not alive, but that should be kept alive for
  //   policy reasons. Keep alive the transitive closure of all
  //   such referents.
  if (policy != NULL) {
    if (mt_processing) {
      RefProcPhase1Task phase1(*this, refs_lists, policy, true /*marks_oops_alive*/);
      task_executor->execute(phase1);
    } else {
      for (uint i = 0; i < _max_num_q; i++) {
        process_phase1(refs_lists[i], policy,
                       is_alive, keep_alive, complete_gc);
      }
    }
  } else { // policy == NULL
    assert(refs_lists != _discoveredSoftRefs,
           "Policy must be specified for soft references.");
  }

  // Phase 2:
  // . Traverse the list and remove any refs whose referents are alive.
  if (mt_processing) {
    RefProcPhase2Task phase2(*this, refs_lists, !discovery_is_atomic() /*marks_oops_alive*/);
    task_executor->execute(phase2);
  } else {
    for (uint i = 0; i < _max_num_q; i++) {
      process_phase2(refs_lists[i], is_alive, keep_alive, complete_gc);
    }
  }

  // Phase 3:
  // . Traverse the list and process referents as appropriate.
  if (mt_processing) {
    RefProcPhase3Task phase3(*this, refs_lists, clear_referent, true /*marks_oops_alive*/);
    task_executor->execute(phase3);
  } else {
    for (uint i = 0; i < _max_num_q; i++) {
      process_phase3(refs_lists[i], clear_referent,
                     is_alive, keep_alive, complete_gc);
    }
  }
}

process_phase1 : 主要是 SoftReference 的處理[只有SoftReference傳入了 policy], 根據 referencePolicy 來校驗當前 Reference 是否應該移出 discoverList 

process_phase2 : 如果 referent 經過了 ygc 還存活着, 那麼 將當前 Reference 移出 discoverList 

process_phase3 : 如果需要 clear_referent, 則清理掉 Reference.referent, 否則 將Reference.referent移動到 存活對象區域 [僅僅FinalReference傳入爲false, FinalReference業務處理的時候還需要referent] 

 

ReferenceProcessor::process_phase3

// Traverse the list and process the referents, by either
// clearing them or keeping them (and their reachable
// closure) alive.
void
ReferenceProcessor::process_phase3(DiscoveredList&    refs_list,
                                   bool               clear_referent,
                                   BoolObjectClosure* is_alive,
                                   OopClosure*        keep_alive,
                                   VoidClosure*       complete_gc) {
  ResourceMark rm;
  DiscoveredListIterator iter(refs_list, keep_alive, is_alive);
  while (iter.has_next()) {
    iter.load_ptrs(DEBUG_ONLY(false /* allow_null_referent */));
    if (clear_referent) {
      // NULL out referent pointer
      iter.clear_referent();
    } else {
      // keep the referent around
      iter.make_referent_alive();
    }
    log_develop_trace(gc, ref)("Adding %sreference (" INTPTR_FORMAT ": %s) as pending",
                               clear_referent ? "cleared " : "", p2i(iter.obj()), iter.obj()->klass()->internal_name());
    assert(iter.obj()->is_oop(UseConcMarkSweepGC), "Adding a bad reference");
    iter.next();
  }
  // Close the reachable set
  complete_gc->do_void();
}

 

經過 ReferenceProcessor::process_discovered_references 處理之後, 最後 還存在於 discoverList 的 Reference 與 Reference.pending 關聯 [gc處理之後, genCollectedHeap.collect_generation裏面 rp->enqueue_discovered_references]

然後 ReferenceHandler 線程會進行下一個階段的處理, 將 Reference.pending 關聯的列表的 所有的 Reference 關聯到 Reference 對應的隊列[參見 Reference$ReferenceHandler]

 

 

2. Test02WeakReferenceAfterGc$Obj 沒有重寫 finalize

首先我們來看一下 Test02WeakReferenceAfterGc$Obj 沒有重寫 finalize 方法的場景 

三個斷點, thread.create_vm 方法末尾一個, defNewGeneration.collect 裏面 ref_processor.process_discovered_references 一個, referenceProcessor.process_discovered_references 裏面 process_discovered_reflist(_discoveredPhantomRefs 一個

當斷點停留在 "referenceProcessor.process_discovered_references" 裏面的時候 

 

2.1 確定當前位置

找到 main 線程, 打印 main 線程的 stackTrace, 從這裏 可以知道 目前是 在 第一個 System.gc 方法的執行期間 

$jerry : thread->print()
"main" #1 prio=5 os_prio=31 tid=0x00007fa786803800 nid=0x2403 waiting on condition [0x0000700007756000]
   java.lang.Thread.State: RUNNABLE
   JavaThread state: _thread_blocked
Thread: 0x00007fa786803800  [0x2403] State: _at_safepoint _has_called_back 0 _at_poll_safepoint 0

$jerry : thread->print_stack()
   JavaThread state: _thread_blocked
	at java.lang.Runtime.gc(java.base/Native Method)
	at java.lang.System.gc(java.base/System.java:1726)
	at com.hx.test02.Test02WeakReferenceAfterGc.main(Test02WeakReferenceAfterGc.java:27)

 

2.2 process_discovered_reflist(_discoveredPhantomRefs 處理之前

然後我們回到 gc 線程, 在斷點處 執行如下腳本, 打印探測到的 PhantomReference 列表 

oop current = ((_discoveredPhantomRefs)->head());
for(int i=0; i<_discoveredPhantomRefs->length(); i++) {
    // tty->print_cr(current->klass()->external_name());
    current.print();
    current = java_lang_ref_Reference::discovered(current);
}

得到結果如下 

jdk.internal.ref.CleanerImpl$PhantomCleanableRef
{0x000000074275dcd0} - klass: 'jdk/internal/ref/CleanerImpl$PhantomCleanableRef'
 - ---- fields (total size 6 words):
 - private strict 'referent' 'Ljava/lang/Object;' @12  a 'java/lang/invoke/ConstantCallSite'{0x00000007404034b0} (e8080696 e84d06ce)
 - volatile strict 'queue' 'Ljava/lang/ref/ReferenceQueue;' @16  a 'java/lang/ref/ReferenceQueue'{0x0000000742683670} (e84d06ce 0)
 - volatile 'next' 'Ljava/lang/ref/Reference;' @20  NULL (0 e84ebb91)
 - private transient strict 'discovered' 'Ljava/lang/ref/Reference;' @24  a 'jdk/internal/ref/CleanerImpl$PhantomCleanableRef'{0x000000074275dc88} (e84ebb91 e84eaf9e)
 - strict 'prev' 'Ljdk/internal/ref/PhantomCleanable;' @28  a 'jdk/internal/ref/CleanerImpl$PhantomCleanableRef'{0x0000000742757cf0} (e84eaf9e e84ebb91)
 - strict 'next' 'Ljdk/internal/ref/PhantomCleanable;' @32  a 'jdk/internal/ref/CleanerImpl$PhantomCleanableRef'{0x000000074275dc88} (e84ebb91 e84d2fe6)
 - private final strict 'list' 'Ljdk/internal/ref/PhantomCleanable;' @36  a 'jdk/internal/ref/CleanerImpl$PhantomCleanableRef'{0x0000000742697f30} (e84d2fe6 e84ec6c6)
 - private final 'action' 'Ljava/lang/Runnable;' @40  a 'java/lang/invoke/MethodHandleNatives$CallSiteContext'{0x0000000742763630} (e84ec6c6 0)
jdk.internal.ref.CleanerImpl$PhantomCleanableRef
{0x000000074275dc88} - klass: 'jdk/internal/ref/CleanerImpl$PhantomCleanableRef'
 - ---- fields (total size 6 words):
 - private strict 'referent' 'Ljava/lang/Object;' @12  a 'java/lang/invoke/ConstantCallSite'{0x000000074036f6a0} (e806ded4 e84d06ce)
 - volatile strict 'queue' 'Ljava/lang/ref/ReferenceQueue;' @16  a 'java/lang/ref/ReferenceQueue'{0x0000000742683670} (e84d06ce 0)
 - volatile 'next' 'Ljava/lang/ref/Reference;' @20  NULL (0 e84eaf9e)
 - private transient strict 'discovered' 'Ljava/lang/ref/Reference;' @24  a 'jdk/internal/ref/CleanerImpl$PhantomCleanableRef'{0x0000000742757cf0} (e84eaf9e e84ebb9a)
 - strict 'prev' 'Ljdk/internal/ref/PhantomCleanable;' @28  a 'jdk/internal/ref/CleanerImpl$PhantomCleanableRef'{0x000000074275dcd0} (e84ebb9a e84eaf95)
 - strict 'next' 'Ljdk/internal/ref/PhantomCleanable;' @32  a 'jdk/internal/ref/CleanerImpl$PhantomCleanableRef'{0x0000000742757ca8} (e84eaf95 e84d2fe6)
 - private final strict 'list' 'Ljdk/internal/ref/PhantomCleanable;' @36  a 'jdk/internal/ref/CleanerImpl$PhantomCleanableRef'{0x0000000742697f30} (e84d2fe6 e84ec6c3)
 - private final 'action' 'Ljava/lang/Runnable;' @40  a 'java/lang/invoke/MethodHandleNatives$CallSiteContext'{0x0000000742763618} (e84ec6c3 0)
jdk.internal.ref.CleanerImpl$PhantomCleanableRef
{0x0000000742757cf0} - klass: 'jdk/internal/ref/CleanerImpl$PhantomCleanableRef'
 - ---- fields (total size 6 words):
 - private strict 'referent' 'Ljava/lang/Object;' @12  a 'java/lang/invoke/ConstantCallSite'{0x0000000740409798} (e80812f3 e84d06ce)
 - volatile strict 'queue' 'Ljava/lang/ref/ReferenceQueue;' @16  a 'java/lang/ref/ReferenceQueue'{0x0000000742683670} (e84d06ce 0)
 - volatile 'next' 'Ljava/lang/ref/Reference;' @20  NULL (0 e84eaf95)
 - private transient strict 'discovered' 'Ljava/lang/ref/Reference;' @24  a 'jdk/internal/ref/CleanerImpl$PhantomCleanableRef'{0x0000000742757ca8} (e84eaf95 e84ea6b5)
 - strict 'prev' 'Ljdk/internal/ref/PhantomCleanable;' @28  a 'jdk/internal/ref/CleanerImpl$PhantomCleanableRef'{0x00000007427535a8} (e84ea6b5 e84ebb9a)
 - strict 'next' 'Ljdk/internal/ref/PhantomCleanable;' @32  a 'jdk/internal/ref/CleanerImpl$PhantomCleanableRef'{0x000000074275dcd0} (e84ebb9a e84d2fe6)
 - private final strict 'list' 'Ljdk/internal/ref/PhantomCleanable;' @36  a 'jdk/internal/ref/CleanerImpl$PhantomCleanableRef'{0x0000000742697f30} (e84d2fe6 e84ebba0)
 - private final 'action' 'Ljava/lang/Runnable;' @40  a 'java/lang/invoke/MethodHandleNatives$CallSiteContext'{0x000000074275dd00} (e84ebba0 0)
jdk.internal.ref.CleanerImpl$PhantomCleanableRef
{0x0000000742757ca8} - klass: 'jdk/internal/ref/CleanerImpl$PhantomCleanableRef'
 - ---- fields (total size 6 words):
 - private strict 'referent' 'Ljava/lang/Object;' @12  a 'java/lang/invoke/ConstantCallSite'{0x000000074036d7f8} (e806daff e84d06ce)
 - volatile strict 'queue' 'Ljava/lang/ref/ReferenceQueue;' @16  a 'java/lang/ref/ReferenceQueue'{0x0000000742683670} (e84d06ce 0)
 - volatile 'next' 'Ljava/lang/ref/Reference;' @20  NULL (0 e84ea6b5)
 - private transient strict 'discovered' 'Ljava/lang/ref/Reference;' @24  a 'jdk/internal/ref/CleanerImpl$PhantomCleanableRef'{0x00000007427535a8} (e84ea6b5 e84ebb91)
 - strict 'prev' 'Ljdk/internal/ref/PhantomCleanable;' @28  a 'jdk/internal/ref/CleanerImpl$PhantomCleanableRef'{0x000000074275dc88} (e84ebb91 e84ea6ac)
 - strict 'next' 'Ljdk/internal/ref/PhantomCleanable;' @32  a 'jdk/internal/ref/CleanerImpl$PhantomCleanableRef'{0x0000000742753560} (e84ea6ac e84d2fe6)
 - private final strict 'list' 'Ljdk/internal/ref/PhantomCleanable;' @36  a 'jdk/internal/ref/CleanerImpl$PhantomCleanableRef'{0x0000000742697f30} (e84d2fe6 e84ebb97)
 - private final 'action' 'Ljava/lang/Runnable;' @40  a 'java/lang/invoke/MethodHandleNatives$CallSiteContext'{0x000000074275dcb8} (e84ebb97 0)
jdk.internal.ref.CleanerImpl$PhantomCleanableRef
{0x00000007427535a8} - klass: 'jdk/internal/ref/CleanerImpl$PhantomCleanableRef'
 - ---- fields (total size 6 words):
 - private strict 'referent' 'Ljava/lang/Object;' @12  a 'java/lang/invoke/ConstantCallSite'{0x0000000740410730} (e80820e6 e84d06ce)
 - volatile strict 'queue' 'Ljava/lang/ref/ReferenceQueue;' @16  a 'java/lang/ref/ReferenceQueue'{0x0000000742683670} (e84d06ce 0)
 - volatile 'next' 'Ljava/lang/ref/Reference;' @20  NULL (0 e84ea6ac)
 - private transient strict 'discovered' 'Ljava/lang/ref/Reference;' @24  a 'jdk/internal/ref/CleanerImpl$PhantomCleanableRef'{0x0000000742753560} (e84ea6ac e84e9c27)
 - strict 'prev' 'Ljdk/internal/ref/PhantomCleanable;' @28  a 'jdk/internal/ref/CleanerImpl$PhantomCleanableRef'{0x000000074274e138} (e84e9c27 e84eaf9e)
 - strict 'next' 'Ljdk/internal/ref/PhantomCleanable;' @32  a 'jdk/internal/ref/CleanerImpl$PhantomCleanableRef'{0x0000000742757cf0} (e84eaf9e e84d2fe6)
 - private final strict 'list' 'Ljdk/internal/ref/PhantomCleanable;' @36  a 'jdk/internal/ref/CleanerImpl$PhantomCleanableRef'{0x0000000742697f30} (e84d2fe6 e84eafa4)
 - private final 'action' 'Ljava/lang/Runnable;' @40  a 'java/lang/invoke/MethodHandleNatives$CallSiteContext'{0x0000000742757d20} (e84eafa4 0)
jdk.internal.ref.CleanerImpl$PhantomCleanableRef
{0x0000000742753560} - klass: 'jdk/internal/ref/CleanerImpl$PhantomCleanableRef'
 - ---- fields (total size 6 words):
 - private strict 'referent' 'Ljava/lang/Object;' @12  a 'java/lang/invoke/ConstantCallSite'{0x000000074036a828} (e806d505 e84d06ce)
 - volatile strict 'queue' 'Ljava/lang/ref/ReferenceQueue;' @16  a 'java/lang/ref/ReferenceQueue'{0x0000000742683670} (e84d06ce 0)
 - volatile 'next' 'Ljava/lang/ref/Reference;' @20  NULL (0 e84e9c27)
 - private transient strict 'discovered' 'Ljava/lang/ref/Reference;' @24  a 'jdk/internal/ref/CleanerImpl$PhantomCleanableRef'{0x000000074274e138} (e84e9c27 e84eaf95)
 - strict 'prev' 'Ljdk/internal/ref/PhantomCleanable;' @28  a 'jdk/internal/ref/CleanerImpl$PhantomCleanableRef'{0x0000000742757ca8} (e84eaf95 e84e9c1e)
 - strict 'next' 'Ljdk/internal/ref/PhantomCleanable;' @32  a 'jdk/internal/ref/CleanerImpl$PhantomCleanableRef'{0x000000074274e0f0} (e84e9c1e e84d2fe6)
 - private final strict 'list' 'Ljdk/internal/ref/PhantomCleanable;' @36  a 'jdk/internal/ref/CleanerImpl$PhantomCleanableRef'{0x0000000742697f30} (e84d2fe6 e84eaf9b)
 - private final 'action' 'Ljava/lang/Runnable;' @40  a 'java/lang/invoke/MethodHandleNatives$CallSiteContext'{0x0000000742757cd8} (e84eaf9b 0)
jdk.internal.ref.CleanerImpl$PhantomCleanableRef
{0x000000074274e138} - klass: 'jdk/internal/ref/CleanerImpl$PhantomCleanableRef'
 - ---- fields (total size 6 words):
 - private strict 'referent' 'Ljava/lang/Object;' @12  a 'java/lang/invoke/ConstantCallSite'{0x00000007404242d0} (e808485a e84d06ce)
 - volatile strict 'queue' 'Ljava/lang/ref/ReferenceQueue;' @16  a 'java/lang/ref/ReferenceQueue'{0x0000000742683670} (e84d06ce 0)
 - volatile 'next' 'Ljava/lang/ref/Reference;' @20  NULL (0 e84e9c1e)
 - private transient strict 'discovered' 'Ljava/lang/ref/Reference;' @24  a 'jdk/internal/ref/CleanerImpl$PhantomCleanableRef'{0x000000074274e0f0} (e84e9c1e e84e8c52)
 - strict 'prev' 'Ljdk/internal/ref/PhantomCleanable;' @28  a 'jdk/internal/ref/CleanerImpl$PhantomCleanableRef'{0x0000000742746290} (e84e8c52 e84ea6b5)
 - strict 'next' 'Ljdk/internal/ref/PhantomCleanable;' @32  a 'jdk/internal/ref/CleanerImpl$PhantomCleanableRef'{0x00000007427535a8} (e84ea6b5 e84d2fe6)
 - private final strict 'list' 'Ljdk/internal/ref/PhantomCleanable;' @36  a 'jdk/internal/ref/CleanerImpl$PhantomCleanableRef'{0x0000000742697f30} (e84d2fe6 e84ea6bb)
 - private final 'action' 'Ljava/lang/Runnable;' @40  a 'java/lang/invoke/MethodHandleNatives$CallSiteContext'{0x00000007427535d8} (e84ea6bb 0)
jdk.internal.ref.CleanerImpl$PhantomCleanableRef
{0x000000074274e0f0} - klass: 'jdk/internal/ref/CleanerImpl$PhantomCleanableRef'
 - ---- fields (total size 6 words):
 - private strict 'referent' 'Ljava/lang/Object;' @12  a 'java/lang/invoke/ConstantCallSite'{0x0000000740359488} (e806b291 e84d06ce)
 - volatile strict 'queue' 'Ljava/lang/ref/ReferenceQueue;' @16  a 'java/lang/ref/ReferenceQueue'{0x0000000742683670} (e84d06ce 0)
 - volatile 'next' 'Ljava/lang/ref/Reference;' @20  NULL (0 e84e8c52)
 - private transient strict 'discovered' 'Ljava/lang/ref/Reference;' @24  a 'jdk/internal/ref/CleanerImpl$PhantomCleanableRef'{0x0000000742746290} (e84e8c52 e84ea6ac)
 - strict 'prev' 'Ljdk/internal/ref/PhantomCleanable;' @28  a 'jdk/internal/ref/CleanerImpl$PhantomCleanableRef'{0x0000000742753560} (e84ea6ac e84e8c49)
 - strict 'next' 'Ljdk/internal/ref/PhantomCleanable;' @32  a 'jdk/internal/ref/CleanerImpl$PhantomCleanableRef'{0x0000000742746248} (e84e8c49 e84d2fe6)
 - private final strict 'list' 'Ljdk/internal/ref/PhantomCleanable;' @36  a 'jdk/internal/ref/CleanerImpl$PhantomCleanableRef'{0x0000000742697f30} (e84d2fe6 e84ea6b2)
 - private final 'action' 'Ljava/lang/Runnable;' @40  a 'java/lang/invoke/MethodHandleNatives$CallSiteContext'{0x0000000742753590} (e84ea6b2 0)
jdk.internal.ref.CleanerImpl$PhantomCleanableRef
{0x0000000742746290} - klass: 'jdk/internal/ref/CleanerImpl$PhantomCleanableRef'
 - ---- fields (total size 6 words):
 - private strict 'referent' 'Ljava/lang/Object;' @12  a 'java/lang/invoke/ConstantCallSite'{0x0000000740431a28} (e8086345 e84d06ce)
 - volatile strict 'queue' 'Ljava/lang/ref/ReferenceQueue;' @16  a 'java/lang/ref/ReferenceQueue'{0x0000000742683670} (e84d06ce 0)
 - volatile 'next' 'Ljava/lang/ref/Reference;' @20  NULL (0 e84e8c49)
 - private transient strict 'discovered' 'Ljava/lang/ref/Reference;' @24  a 'jdk/internal/ref/CleanerImpl$PhantomCleanableRef'{0x0000000742746248} (e84e8c49 e84e77b7)
 - strict 'prev' 'Ljdk/internal/ref/PhantomCleanable;' @28  a 'jdk/internal/ref/CleanerImpl$PhantomCleanableRef'{0x000000074273bdb8} (e84e77b7 e84e9c27)
 - strict 'next' 'Ljdk/internal/ref/PhantomCleanable;' @32  a 'jdk/internal/ref/CleanerImpl$PhantomCleanableRef'{0x000000074274e138} (e84e9c27 e84d2fe6)
 - private final strict 'list' 'Ljdk/internal/ref/PhantomCleanable;' @36  a 'jdk/internal/ref/CleanerImpl$PhantomCleanableRef'{0x0000000742697f30} (e84d2fe6 e84e9c2d)
 - private final 'action' 'Ljava/lang/Runnable;' @40  a 'java/lang/invoke/MethodHandleNatives$CallSiteContext'{0x000000074274e168} (e84e9c2d 0)
jdk.internal.ref.CleanerImpl$PhantomCleanableRef
{0x0000000742746248} - klass: 'jdk/internal/ref/CleanerImpl$PhantomCleanableRef'
 - ---- fields (total size 6 words):
 - private strict 'referent' 'Ljava/lang/Object;' @12  a 'java/lang/invoke/ConstantCallSite'{0x0000000740351e60} (e806a3cc e84d06ce)
 - volatile strict 'queue' 'Ljava/lang/ref/ReferenceQueue;' @16  a 'java/lang/ref/ReferenceQueue'{0x0000000742683670} (e84d06ce 0)
 - volatile 'next' 'Ljava/lang/ref/Reference;' @20  NULL (0 e84e77b7)
 - private transient strict 'discovered' 'Ljava/lang/ref/Reference;' @24  a 'jdk/internal/ref/CleanerImpl$PhantomCleanableRef'{0x000000074273bdb8} (e84e77b7 e84e9c1e)
 - strict 'prev' 'Ljdk/internal/ref/PhantomCleanable;' @28  a 'jdk/internal/ref/CleanerImpl$PhantomCleanableRef'{0x000000074274e0f0} (e84e9c1e e84e77ae)
 - strict 'next' 'Ljdk/internal/ref/PhantomCleanable;' @32  a 'jdk/internal/ref/CleanerImpl$PhantomCleanableRef'{0x000000074273bd70} (e84e77ae e84d2fe6)
 - private final strict 'list' 'Ljdk/internal/ref/PhantomCleanable;' @36  a 'jdk/internal/ref/CleanerImpl$PhantomCleanableRef'{0x0000000742697f30} (e84d2fe6 e84e9c24)
 - private final 'action' 'Ljava/lang/Runnable;' @40  a 'java/lang/invoke/MethodHandleNatives$CallSiteContext'{0x000000074274e120} (e84e9c24 0)
jdk.internal.ref.CleanerImpl$PhantomCleanableRef
{0x000000074273bdb8} - klass: 'jdk/internal/ref/CleanerImpl$PhantomCleanableRef'
 - ---- fields (total size 6 words):
 - private strict 'referent' 'Ljava/lang/Object;' @12  a 'java/lang/invoke/ConstantCallSite'{0x0000000740438e00} (e80871c0 e84d06ce)
 - volatile strict 'queue' 'Ljava/lang/ref/ReferenceQueue;' @16  a 'java/lang/ref/ReferenceQueue'{0x0000000742683670} (e84d06ce 0)
 - volatile 'next' 'Ljava/lang/ref/Reference;' @20  NULL (0 e84e77ae)
 - private transient strict 'discovered' 'Ljava/lang/ref/Reference;' @24  a 'jdk/internal/ref/CleanerImpl$PhantomCleanableRef'{0x000000074273bd70} (e84e77ae e84e571d)
 - strict 'prev' 'Ljdk/internal/ref/PhantomCleanable;' @28  a 'jdk/internal/ref/CleanerImpl$PhantomCleanableRef'{0x000000074272b8e8} (e84e571d e84e8c52)
 - strict 'next' 'Ljdk/internal/ref/PhantomCleanable;' @32  a 'jdk/internal/ref/CleanerImpl$PhantomCleanableRef'{0x0000000742746290} (e84e8c52 e84d2fe6)
 - private final strict 'list' 'Ljdk/internal/ref/PhantomCleanable;' @36  a 'jdk/internal/ref/CleanerImpl$PhantomCleanableRef'{0x0000000742697f30} (e84d2fe6 e84e8c58)
 - private final 'action' 'Ljava/lang/Runnable;' @40  a 'java/lang/invoke/MethodHandleNatives$CallSiteContext'{0x00000007427462c0} (e84e8c58 0)
jdk.internal.ref.CleanerImpl$PhantomCleanableRef
{0x000000074273bd70} - klass: 'jdk/internal/ref/CleanerImpl$PhantomCleanableRef'
 - ---- fields (total size 6 words):
 - private strict 'referent' 'Ljava/lang/Object;' @12  a 'java/lang/invoke/ConstantCallSite'{0x000000074019cef8} (e80339df e84d06ce)
 - volatile strict 'queue' 'Ljava/lang/ref/ReferenceQueue;' @16  a 'java/lang/ref/ReferenceQueue'{0x0000000742683670} (e84d06ce 0)
 - volatile 'next' 'Ljava/lang/ref/Reference;' @20  NULL (0 e84e571d)
 - private transient strict 'discovered' 'Ljava/lang/ref/Reference;' @24  a 'jdk/internal/ref/CleanerImpl$PhantomCleanableRef'{0x000000074272b8e8} (e84e571d e84e8c49)
 - strict 'prev' 'Ljdk/internal/ref/PhantomCleanable;' @28  a 'jdk/internal/ref/CleanerImpl$PhantomCleanableRef'{0x0000000742746248} (e84e8c49 e84e5714)
 - strict 'next' 'Ljdk/internal/ref/PhantomCleanable;' @32  a 'jdk/internal/ref/CleanerImpl$PhantomCleanableRef'{0x000000074272b8a0} (e84e5714 e84d2fe6)
 - private final strict 'list' 'Ljdk/internal/ref/PhantomCleanable;' @36  a 'jdk/internal/ref/CleanerImpl$PhantomCleanableRef'{0x0000000742697f30} (e84d2fe6 e84e8c4f)
 - private final 'action' 'Ljava/lang/Runnable;' @40  a 'java/lang/invoke/MethodHandleNatives$CallSiteContext'{0x0000000742746278} (e84e8c4f 0)
jdk.internal.ref.CleanerImpl$PhantomCleanableRef
{0x000000074272b8e8} - klass: 'jdk/internal/ref/CleanerImpl$PhantomCleanableRef'
 - ---- fields (total size 6 words):
 - private strict 'referent' 'Ljava/lang/Object;' @12  a 'java/lang/invoke/ConstantCallSite'{0x0000000740445580} (e8088ab0 e84d06ce)
 - volatile strict 'queue' 'Ljava/lang/ref/ReferenceQueue;' @16  a 'java/lang/ref/ReferenceQueue'{0x0000000742683670} (e84d06ce 0)
 - volatile 'next' 'Ljava/lang/ref/Reference;' @20  NULL (0 e84e5714)
 - private transient strict 'discovered' 'Ljava/lang/ref/Reference;' @24  a 'jdk/internal/ref/CleanerImpl$PhantomCleanableRef'{0x000000074272b8a0} (e84e5714 e84e2db8)
 - strict 'prev' 'Ljdk/internal/ref/PhantomCleanable;' @28  a 'jdk/internal/ref/CleanerImpl$PhantomCleanableRef'{0x0000000742716dc0} (e84e2db8 e84e77b7)
 - strict 'next' 'Ljdk/internal/ref/PhantomCleanable;' @32  a 'jdk/internal/ref/CleanerImpl$PhantomCleanableRef'{0x000000074273bdb8} (e84e77b7 e84d2fe6)
 - private final strict 'list' 'Ljdk/internal/ref/PhantomCleanable;' @36  a 'jdk/internal/ref/CleanerImpl$PhantomCleanableRef'{0x0000000742697f30} (e84d2fe6 e84e77bd)
 - private final 'action' 'Ljava/lang/Runnable;' @40  a 'java/lang/invoke/MethodHandleNatives$CallSiteContext'{0x000000074273bde8} (e84e77bd 0)
jdk.internal.ref.CleanerImpl$PhantomCleanableRef
{0x000000074272b8a0} - klass: 'jdk/internal/ref/CleanerImpl$PhantomCleanableRef'
 - ---- fields (total size 6 words):
 - private strict 'referent' 'Ljava/lang/Object;' @12  a 'java/lang/invoke/ConstantCallSite'{0x0000000740193d48} (e80327a9 e84d06ce)
 - volatile strict 'queue' 'Ljava/lang/ref/ReferenceQueue;' @16  a 'java/lang/ref/ReferenceQueue'{0x0000000742683670} (e84d06ce 0)
 - volatile 'next' 'Ljava/lang/ref/Reference;' @20  NULL (0 e84e2db8)
 - private transient strict 'discovered' 'Ljava/lang/ref/Reference;' @24  a 'jdk/internal/ref/CleanerImpl$PhantomCleanableRef'{0x0000000742716dc0} (e84e2db8 e84e77ae)
 - strict 'prev' 'Ljdk/internal/ref/PhantomCleanable;' @28  a 'jdk/internal/ref/CleanerImpl$PhantomCleanableRef'{0x000000074273bd70} (e84e77ae e84e2daf)
 - strict 'next' 'Ljdk/internal/ref/PhantomCleanable;' @32  a 'jdk/internal/ref/CleanerImpl$PhantomCleanableRef'{0x0000000742716d78} (e84e2daf e84d2fe6)
 - private final strict 'list' 'Ljdk/internal/ref/PhantomCleanable;' @36  a 'jdk/internal/ref/CleanerImpl$PhantomCleanableRef'{0x0000000742697f30} (e84d2fe6 e84e77b4)
 - private final 'action' 'Ljava/lang/Runnable;' @40  a 'java/lang/invoke/MethodHandleNatives$CallSiteContext'{0x000000074273bda0} (e84e77b4 0)
jdk.internal.ref.CleanerImpl$PhantomCleanableRef
{0x0000000742716dc0} - klass: 'jdk/internal/ref/CleanerImpl$PhantomCleanableRef'
 - ---- fields (total size 6 words):
 - private strict 'referent' 'Ljava/lang/Object;' @12  a 'java/lang/invoke/ConstantCallSite'{0x0000000740449470} (e808928e e84d06ce)
 - volatile strict 'queue' 'Ljava/lang/ref/ReferenceQueue;' @16  a 'java/lang/ref/ReferenceQueue'{0x0000000742683670} (e84d06ce 0)
 - volatile 'next' 'Ljava/lang/ref/Reference;' @20  NULL (0 e84e2daf)
 - private transient strict 'discovered' 'Ljava/lang/ref/Reference;' @24  a 'jdk/internal/ref/CleanerImpl$PhantomCleanableRef'{0x0000000742716d78} (e84e2daf e84de643)
 - strict 'prev' 'Ljdk/internal/ref/PhantomCleanable;' @28  a 'jdk/internal/ref/CleanerImpl$PhantomCleanableRef'{0x00000007426f3218} (e84de643 e84e571d)
 - strict 'next' 'Ljdk/internal/ref/PhantomCleanable;' @32  a 'jdk/internal/ref/CleanerImpl$PhantomCleanableRef'{0x000000074272b8e8} (e84e571d e84d2fe6)
 - private final strict 'list' 'Ljdk/internal/ref/PhantomCleanable;' @36  a 'jdk/internal/ref/CleanerImpl$PhantomCleanableRef'{0x0000000742697f30} (e84d2fe6 e84e5723)
 - private final 'action' 'Ljava/lang/Runnable;' @40  a 'java/lang/invoke/MethodHandleNatives$CallSiteContext'{0x000000074272b918} (e84e5723 0)
jdk.internal.ref.CleanerImpl$PhantomCleanableRef
{0x0000000742716d78} - klass: 'jdk/internal/ref/CleanerImpl$PhantomCleanableRef'
 - ---- fields (total size 6 words):
 - private strict 'referent' 'Ljava/lang/Object;' @12  a 'java/lang/invoke/ConstantCallSite'{0x0000000740188140} (e8031028 e84d06ce)
 - volatile strict 'queue' 'Ljava/lang/ref/ReferenceQueue;' @16  a 'java/lang/ref/ReferenceQueue'{0x0000000742683670} (e84d06ce 0)
 - volatile 'next' 'Ljava/lang/ref/Reference;' @20  NULL (0 e84de643)
 - private transient strict 'discovered' 'Ljava/lang/ref/Reference;' @24  a 'jdk/internal/ref/CleanerImpl$PhantomCleanableRef'{0x00000007426f3218} (e84de643 e84e5714)
 - strict 'prev' 'Ljdk/internal/ref/PhantomCleanable;' @28  a 'jdk/internal/ref/CleanerImpl$PhantomCleanableRef'{0x000000074272b8a0} (e84e5714 e84de63a)
 - strict 'next' 'Ljdk/internal/ref/PhantomCleanable;' @32  a 'jdk/internal/ref/CleanerImpl$PhantomCleanableRef'{0x00000007426f31d0} (e84de63a e84d2fe6)
 - private final strict 'list' 'Ljdk/internal/ref/PhantomCleanable;' @36  a 'jdk/internal/ref/CleanerImpl$PhantomCleanableRef'{0x0000000742697f30} (e84d2fe6 e84e571a)
 - private final 'action' 'Ljava/lang/Runnable;' @40  a 'java/lang/invoke/MethodHandleNatives$CallSiteContext'{0x000000074272b8d0} (e84e571a 0)
jdk.internal.ref.CleanerImpl$PhantomCleanableRef
{0x00000007426f3218} - klass: 'jdk/internal/ref/CleanerImpl$PhantomCleanableRef'
 - ---- fields (total size 6 words):
 - private strict 'referent' 'Ljava/lang/Object;' @12  a 'java/lang/invoke/ConstantCallSite'{0x000000074044c550} (e80898aa e84d06ce)
 - volatile strict 'queue' 'Ljava/lang/ref/ReferenceQueue;' @16  a 'java/lang/ref/ReferenceQueue'{0x0000000742683670} (e84d06ce 0)
 - volatile 'next' 'Ljava/lang/ref/Reference;' @20  NULL (0 e84de63a)
 - private transient strict 'discovered' 'Ljava/lang/ref/Reference;' @24  a 'jdk/internal/ref/CleanerImpl$PhantomCleanableRef'{0x00000007426f31d0} (e84de63a e84da7f7)
 - strict 'prev' 'Ljdk/internal/ref/PhantomCleanable;' @28  a 'jdk/internal/ref/CleanerImpl$PhantomCleanableRef'{0x00000007426d3fb8} (e84da7f7 e84e2db8)
 - strict 'next' 'Ljdk/internal/ref/PhantomCleanable;' @32  a 'jdk/internal/ref/CleanerImpl$PhantomCleanableRef'{0x0000000742716dc0} (e84e2db8 e84d2fe6)
 - private final strict 'list' 'Ljdk/internal/ref/PhantomCleanable;' @36  a 'jdk/internal/ref/CleanerImpl$PhantomCleanableRef'{0x0000000742697f30} (e84d2fe6 e84e2dbe)
 - private final 'action' 'Ljava/lang/Runnable;' @40  a 'java/lang/invoke/MethodHandleNatives$CallSiteContext'{0x0000000742716df0} (e84e2dbe 0)
jdk.internal.ref.CleanerImpl$PhantomCleanableRef
{0x00000007426f31d0} - klass: 'jdk/internal/ref/CleanerImpl$PhantomCleanableRef'
 - ---- fields (total size 6 words):
 - private strict 'referent' 'Ljava/lang/Object;' @12  a 'java/lang/invoke/ConstantCallSite'{0x0000000740172188} (e802e431 e84d06ce)
 - volatile strict 'queue' 'Ljava/lang/ref/ReferenceQueue;' @16  a 'java/lang/ref/ReferenceQueue'{0x0000000742683670} (e84d06ce 0)
 - volatile 'next' 'Ljava/lang/ref/Reference;' @20  NULL (0 e84da7f7)
 - private transient strict 'discovered' 'Ljava/lang/ref/Reference;' @24  a 'jdk/internal/ref/CleanerImpl$PhantomCleanableRef'{0x00000007426d3fb8} (e84da7f7 e84e2daf)
 - strict 'prev' 'Ljdk/internal/ref/PhantomCleanable;' @28  a 'jdk/internal/ref/CleanerImpl$PhantomCleanableRef'{0x0000000742716d78} (e84e2daf e84da7f1)
 - strict 'next' 'Ljdk/internal/ref/PhantomCleanable;' @32  a 'jdk/internal/ref/CleanerImpl$PhantomCleanableRef'{0x00000007426d3f88} (e84da7f1 e84d2fe6)
 - private final strict 'list' 'Ljdk/internal/ref/PhantomCleanable;' @36  a 'jdk/internal/ref/CleanerImpl$PhantomCleanableRef'{0x0000000742697f30} (e84d2fe6 e84e2db5)
 - private final 'action' 'Ljava/lang/Runnable;' @40  a 'java/lang/invoke/MethodHandleNatives$CallSiteContext'{0x0000000742716da8} (e84e2db5 0)
jdk.internal.ref.CleanerImpl$PhantomCleanableRef
{0x00000007426d3fb8} - klass: 'jdk/internal/ref/CleanerImpl$PhantomCleanableRef'
 - ---- fields (total size 6 words):
 - private strict 'referent' 'Ljava/lang/Object;' @12  a 'java/lang/invoke/ConstantCallSite'{0x000000074044ed28} (e8089da5 e84d06ce)
 - volatile strict 'queue' 'Ljava/lang/ref/ReferenceQueue;' @16  a 'java/lang/ref/ReferenceQueue'{0x0000000742683670} (e84d06ce 0)
 - volatile 'next' 'Ljava/lang/ref/Reference;' @20  NULL (0 e84da7f1)
 - private transient strict 'discovered' 'Ljava/lang/ref/Reference;' @24  a 'jdk/internal/ref/CleanerImpl$PhantomCleanableRef'{0x00000007426d3f88} (e84da7f1 e84d6a6d)
 - strict 'prev' 'Ljdk/internal/ref/PhantomCleanable;' @28  a 'jdk/internal/ref/CleanerImpl$PhantomCleanableRef'{0x00000007426b5368} (e84d6a6d e84de643)
 - strict 'next' 'Ljdk/internal/ref/PhantomCleanable;' @32  a 'jdk/internal/ref/CleanerImpl$PhantomCleanableRef'{0x00000007426f3218} (e84de643 e84d2fe6)
 - private final strict 'list' 'Ljdk/internal/ref/PhantomCleanable;' @36  a 'jdk/internal/ref/CleanerImpl$PhantomCleanableRef'{0x0000000742697f30} (e84d2fe6 e84de649)
 - private final 'action' 'Ljava/lang/Runnable;' @40  a 'java/lang/invoke/MethodHandleNatives$CallSiteContext'{0x00000007426f3248} (e84de649 0)
jdk.internal.ref.CleanerImpl$PhantomCleanableRef
{0x00000007426d3f88} - klass: 'jdk/internal/ref/CleanerImpl$PhantomCleanableRef'
 - ---- fields (total size 6 words):
 - private strict 'referent' 'Ljava/lang/Object;' @12  a 'sun/nio/fs/NativeBuffer'{0x0000000740036f20} (e8006de4 e84d06ce)
 - volatile strict 'queue' 'Ljava/lang/ref/ReferenceQueue;' @16  a 'java/lang/ref/ReferenceQueue'{0x0000000742683670} (e84d06ce 0)
 - volatile 'next' 'Ljava/lang/ref/Reference;' @20  NULL (0 e84d6a6d)
 - private transient strict 'discovered' 'Ljava/lang/ref/Reference;' @24  a 'jdk/internal/ref/CleanerImpl$PhantomCleanableRef'{0x00000007426b5368} (e84d6a6d e84de63a)
 - strict 'prev' 'Ljdk/internal/ref/PhantomCleanable;' @28  a 'jdk/internal/ref/CleanerImpl$PhantomCleanableRef'{0x00000007426f31d0} (e84de63a e84d6a68)
 - strict 'next' 'Ljdk/internal/ref/PhantomCleanable;' @32  a 'jdk/internal/ref/CleanerImpl$CleanerCleanable'{0x00000007426b5340} (e84d6a68 e84d2fe6)
 - private final strict 'list' 'Ljdk/internal/ref/PhantomCleanable;' @36  a 'jdk/internal/ref/CleanerImpl$PhantomCleanableRef'{0x0000000742697f30} (e84d2fe6 e84de640)
 - private final 'action' 'Ljava/lang/Runnable;' @40  a 'sun/nio/fs/NativeBuffer$Deallocator'{0x00000007426f3200} (e84de640 0)
jdk.internal.ref.CleanerImpl$PhantomCleanableRef
{0x00000007426b5368} - klass: 'jdk/internal/ref/CleanerImpl$PhantomCleanableRef'
 - ---- fields (total size 6 words):
 - private strict 'referent' 'Ljava/lang/Object;' @12  a 'java/lang/invoke/ConstantCallSite'{0x000000074045dfe8} (e808bbfd e84d06ce)
 - volatile strict 'queue' 'Ljava/lang/ref/ReferenceQueue;' @16  a 'java/lang/ref/ReferenceQueue'{0x0000000742683670} (e84d06ce 0)
 - volatile 'next' 'Ljava/lang/ref/Reference;' @20  NULL (0 e84d2df2)
 - private transient strict 'discovered' 'Ljava/lang/ref/Reference;' @24  a 'jdk/internal/ref/Cleaner'{0x0000000742696f90} (e84d2df2 e84d2fe6)
 - strict 'prev' 'Ljdk/internal/ref/PhantomCleanable;' @28  a 'jdk/internal/ref/CleanerImpl$PhantomCleanableRef'{0x0000000742697f30} (e84d2fe6 e84da7f7)
 - strict 'next' 'Ljdk/internal/ref/PhantomCleanable;' @32  a 'jdk/internal/ref/CleanerImpl$PhantomCleanableRef'{0x00000007426d3fb8} (e84da7f7 e84d2fe6)
 - private final strict 'list' 'Ljdk/internal/ref/PhantomCleanable;' @36  a 'jdk/internal/ref/CleanerImpl$PhantomCleanableRef'{0x0000000742697f30} (e84d2fe6 e84da7fd)
 - private final 'action' 'Ljava/lang/Runnable;' @40  a 'java/lang/invoke/MethodHandleNatives$CallSiteContext'{0x00000007426d3fe8} (e84da7fd 0)
jdk.internal.ref.Cleaner
{0x0000000742696f90} - klass: 'jdk/internal/ref/Cleaner'
 - ---- fields (total size 5 words):
 - private strict 'referent' 'Ljava/lang/Object;' @12  a 'java/nio/DirectByteBuffer'{0x0000000740189c90} (e8031392 e84d2dee)
 - volatile strict 'queue' 'Ljava/lang/ref/ReferenceQueue;' @16  a 'java/lang/ref/ReferenceQueue'{0x0000000742696f70} (e84d2dee 0)
 - volatile 'next' 'Ljava/lang/ref/Reference;' @20  NULL (0 e84d0774)
 - private transient strict 'discovered' 'Ljava/lang/ref/Reference;' @24  a 'java/lang/ref/PhantomReference'{0x0000000742683ba0} (e84d0774 0)
 - private 'next' 'Ljdk/internal/ref/Cleaner;' @28  NULL (0 0)
 - private 'prev' 'Ljdk/internal/ref/Cleaner;' @32  NULL (0 e84d60c1)
 - private final 'thunk' 'Ljava/lang/Runnable;' @36  a 'java/nio/DirectByteBuffer$Deallocator'{0x00000007426b0608} (e84d60c1 9)
java.lang.ref.PhantomReference
{0x0000000742683ba0} - klass: 'java/lang/ref/PhantomReference'
 - ---- fields (total size 4 words):
 - private strict 'referent' 'Ljava/lang/Object;' @12  a 'com/hx/test02/Test02WeakReferenceAfterGc$Obj'{0x00000007404e1df0} (e809c3be e84d0770)
 - volatile strict 'queue' 'Ljava/lang/ref/ReferenceQueue;' @16  a 'java/lang/ref/ReferenceQueue'{0x0000000742683b80} (e84d0770 0)
 - volatile 'next' 'Ljava/lang/ref/Reference;' @20  NULL (0 e84d0774)
 - private transient strict 'discovered' 'Ljava/lang/ref/Reference;' @24  a 'java/lang/ref/PhantomReference'{0x0000000742683ba0} (e84d0774 0)

這裏面的內容很多, 但是實際上我們只用關心 最後一個 "private strict 'referent' 'Ljava/lang/Object;' @12  a 'com/hx/test02/Test02WeakReferenceAfterGc$Obj'{0x00000007404e1df0} (e809c3be e84d0770)"

在之後的截圖, 可能我這邊會 去掉 部分與本主題無關的的引用的信息, 方便查看 , 並且都會加上備註 

可以發現 gc 的時候找到了 我們的這個 “ref” 引用, 然後 這裏是待處理的狀態, 那麼會怎麼處理呢 ? 

 

1.3 process_discovered_reflist(_discoveredPhantomRefs 處理之後

然後 斷點下走一步, 我們再來看一下 _discoveredPhantomRefs 列表的數據 

同樣執行 上面查看 _discoveredPhantomRefs 列表的代碼片, 得到如下結果 

## 省略掉部分無關的 CleanerImpl$PhantomCleanableRef, Cleaner
java.lang.ref.PhantomReference 
{0x0000000742683ba0} - klass: 'java/lang/ref/PhantomReference'
 - ---- fields (total size 4 words):
 - private strict 'referent' 'Ljava/lang/Object;' @12  NULL (0 e84d0770)
 - volatile strict 'queue' 'Ljava/lang/ref/ReferenceQueue;' @16  a 'java/lang/ref/ReferenceQueue'{0x0000000742683b80} (e84d0770 0)
 - volatile 'next' 'Ljava/lang/ref/Reference;' @20  NULL (0 e84d0774)
 - private transient strict 'discovered' 'Ljava/lang/ref/Reference;' @24  a 'java/lang/ref/PhantomReference'{0x0000000742683ba0} (e84d0774 0)

根據上面的 oop 的地址 0x0000000742683ba0 可以定位到上面的 PhantomReference 就是 "process_discovered_reflist(_discoveredPhantomRefs" 處理之後的數據了 

可以發現 變化在於 referent 被置爲 NULL 了 

tips : 但是 在openjdk8對於 PhantomReference 的 process_phase3的處理是不會將 referent 置爲 NULL 的, 參見 : http://hg.openjdk.java.net/jdk8/jdk8/hotspot/file/87ee5ee27509/src/share/vm/memory/referenceProcessor.cpp 249-252行

這個是由於 process_phase3 的過程中發現 referent 已經 gg 了, 然後就清理掉了 ref.referent 然後並且把它留在了 discoverList, 等待後續的步驟進行處理 

 

然後後續 vm 將還存在的 discoverList 的引用關聯到 Reference.pending 的列表上面 

ReferenceHandler 將 Reference.pending 列表上的 Reference 放到 該Reference註冊的 ReferenceQueue, 對應於我們這裏 ref 會被放到 referenceQueue 

然後 我們這裏的消費線程, 從隊列裏面取引用, 拿到數據, 打印日誌 "gc will collect ... "

 

這就是一個相對比較常規的流程 

 

 

3. Test02WeakReferenceAfterGc$Obj 重寫 finalize

增加一個 referenceProcessor.process_discovered_references 裏面 process_discovered_reflist(_discoveredFinalRefs 斷點 

3.1 process_discovered_reflist(_discoveredFinalRefs 處理之前

打印 _discoveredFinalRefs 列表的數據如下 

java.lang.ref.Finalizer 
{0x00000007426cf9e8} - klass: 'java/lang/ref/Finalizer'
 - ---- fields (total size 5 words):
 - private strict 'referent' 'Ljava/lang/Object;' @12  a 'java/io/FileInputStream'{0x00000007404dbdb0} (e809b7b6 e84d073a)
 - volatile strict 'queue' 'Ljava/lang/ref/ReferenceQueue;' @16  a 'java/lang/ref/ReferenceQueue'{0x00000007426839d0} (e84d073a 0)
 - volatile 'next' 'Ljava/lang/ref/Reference;' @20  NULL (0 e84d6618)
 - private transient strict 'discovered' 'Ljava/lang/ref/Reference;' @24  a 'java/lang/ref/Finalizer'{0x00000007426b30c0} (e84d6618 e84de472)
 - private 'next' 'Ljava/lang/ref/Finalizer;' @28  a 'java/lang/ref/Finalizer'{0x00000007426f2390} (e84de472 e84d6618)
 - private 'prev' 'Ljava/lang/ref/Finalizer;' @32  a 'java/lang/ref/Finalizer'{0x00000007426b30c0} (e84d6618 0)
java.lang.ref.Finalizer 
{0x00000007426b30c0} - klass: 'java/lang/ref/Finalizer'
 - ---- fields (total size 5 words):
 - private strict 'referent' 'Ljava/lang/Object;' @12  a 'java/io/FileInputStream'{0x00000007404e14d8} (e809c29b e84d073a)
 - volatile strict 'queue' 'Ljava/lang/ref/ReferenceQueue;' @16  a 'java/lang/ref/ReferenceQueue'{0x00000007426839d0} (e84d073a 0)
 - volatile 'next' 'Ljava/lang/ref/Reference;' @20  NULL (0 e84d2f87)
 - private transient strict 'discovered' 'Ljava/lang/ref/Reference;' @24  a 'java/lang/ref/Finalizer'{0x0000000742697c38} (e84d2f87 e84d9f3d)
 - private 'next' 'Ljava/lang/ref/Finalizer;' @28  a 'java/lang/ref/Finalizer'{0x00000007426cf9e8} (e84d9f3d e84d2f87)
 - private 'prev' 'Ljava/lang/ref/Finalizer;' @32  a 'java/lang/ref/Finalizer'{0x0000000742697c38} (e84d2f87 0)
java.lang.ref.Finalizer 
{0x0000000742697c38} - klass: 'java/lang/ref/Finalizer'
 - ---- fields (total size 5 words):
 - private strict 'referent' 'Ljava/lang/Object;' @12  a 'com/hx/test02/Test02WeakReferenceAfterGc$Obj'{0x00000007404e1fa0} (e809c3f4 e84d073a)
 - volatile strict 'queue' 'Ljava/lang/ref/ReferenceQueue;' @16  a 'java/lang/ref/ReferenceQueue'{0x00000007426839d0} (e84d073a 0)
 - volatile 'next' 'Ljava/lang/ref/Reference;' @20  NULL (0 e84d2f87)
 - private transient strict 'discovered' 'Ljava/lang/ref/Reference;' @24  a 'java/lang/ref/Finalizer'{0x0000000742697c38} (e84d2f87 e84d6618)
 - private 'next' 'Ljava/lang/ref/Finalizer;' @28  a 'java/lang/ref/Finalizer'{0x00000007426b30c0} (e84d6618 0)
 - private 'prev' 'Ljava/lang/ref/Finalizer;' @32  NULL (0 0)

第三個 FinalReference 爲 vm爲 我們創建的 Obj 實例 o 創建的 Finalzer 

 

3.2 process_discovered_reflist(_discoveredFinalRefs 處理之後

打印 _discoveredFinalRefs 列表的數據如下 

java.lang.ref.Finalizer
{0x00000007426cf9e8} - klass: 'java/lang/ref/Finalizer'
 - ---- fields (total size 5 words):
 - private strict 'referent' 'Ljava/lang/Object;' @12  a 'java/io/FileInputStream'{0x0000000742770968} (e84ee12d e84d073a)
 - volatile strict 'queue' 'Ljava/lang/ref/ReferenceQueue;' @16  a 'java/lang/ref/ReferenceQueue'{0x00000007426839d0} (e84d073a 0)
 - volatile 'next' 'Ljava/lang/ref/Reference;' @20  NULL (0 e84d6618)
 - private transient strict 'discovered' 'Ljava/lang/ref/Reference;' @24  a 'java/lang/ref/Finalizer'{0x00000007426b30c0} (e84d6618 e84de472)
 - private 'next' 'Ljava/lang/ref/Finalizer;' @28  a 'java/lang/ref/Finalizer'{0x00000007426f2390} (e84de472 e84d6618)
 - private 'prev' 'Ljava/lang/ref/Finalizer;' @32  a 'java/lang/ref/Finalizer'{0x00000007426b30c0} (e84d6618 0)
java.lang.ref.Finalizer
{0x00000007426b30c0} - klass: 'java/lang/ref/Finalizer'
 - ---- fields (total size 5 words):
 - private strict 'referent' 'Ljava/lang/Object;' @12  a 'java/io/FileInputStream'{0x0000000742770988} (e84ee131 e84d073a)
 - volatile strict 'queue' 'Ljava/lang/ref/ReferenceQueue;' @16  a 'java/lang/ref/ReferenceQueue'{0x00000007426839d0} (e84d073a 0)
 - volatile 'next' 'Ljava/lang/ref/Reference;' @20  NULL (0 e84d2f87)
 - private transient strict 'discovered' 'Ljava/lang/ref/Reference;' @24  a 'java/lang/ref/Finalizer'{0x0000000742697c38} (e84d2f87 e84d9f3d)
 - private 'next' 'Ljava/lang/ref/Finalizer;' @28  a 'java/lang/ref/Finalizer'{0x00000007426cf9e8} (e84d9f3d e84d2f87)
 - private 'prev' 'Ljava/lang/ref/Finalizer;' @32  a 'java/lang/ref/Finalizer'{0x0000000742697c38} (e84d2f87 0)
java.lang.ref.Finalizer
{0x0000000742697c38} - klass: 'java/lang/ref/Finalizer'
 - ---- fields (total size 5 words):
 - private strict 'referent' 'Ljava/lang/Object;' @12  a 'com/hx/test02/Test02WeakReferenceAfterGc$Obj'{0x00000007427709a8} (e84ee135 e84d073a)
 - volatile strict 'queue' 'Ljava/lang/ref/ReferenceQueue;' @16  a 'java/lang/ref/ReferenceQueue'{0x00000007426839d0} (e84d073a 0)
 - volatile 'next' 'Ljava/lang/ref/Reference;' @20  NULL (0 e84d2f87)
 - private transient strict 'discovered' 'Ljava/lang/ref/Reference;' @24  a 'java/lang/ref/Finalizer'{0x0000000742697c38} (e84d2f87 e84d6618)
 - private 'next' 'Ljava/lang/ref/Finalizer;' @28  a 'java/lang/ref/Finalizer'{0x00000007426b30c0} (e84d6618 0)
 - private 'prev' 'Ljava/lang/ref/Finalizer;' @32  NULL (0 0)

可以看到 這個 我們創建的 Obj 實例 o 對應的 FinalReference 依然存在於 discoverList, 這就意味着之後 這個 FinalReference 之後會被添加到 它對應的 ReferenceQueue, 然後 FinalizerThread 線程 進行 finalize 的相關業務處理 

注意, 這個 處理的過程的 process_phase3 這裏傳遞的 clear_referent 爲 false, 表示 需要將 Obj 實例 o, 複製到 存活區, FinalReference 依賴這個對象 

 

3.3 process_discovered_reflist(_discoveredPhantomRefs 處理之前

打印 _discoveredPhantomRefs 列表的數據如下 

## 省略掉部分無關的 CleanerImpl$PhantomCleanableRef, Cleaner
java.lang.ref.PhantomReference
{0x0000000742683bb8} - klass: 'java/lang/ref/PhantomReference'
 - ---- fields (total size 4 words):
 - private strict 'referent' 'Ljava/lang/Object;' @12  a 'com/hx/test02/Test02WeakReferenceAfterGc$Obj'{0x00000007404e1fa0} (e809c3f4 e84d0773)
 - volatile strict 'queue' 'Ljava/lang/ref/ReferenceQueue;' @16  a 'java/lang/ref/ReferenceQueue'{0x0000000742683b98} (e84d0773 0)
 - volatile 'next' 'Ljava/lang/ref/Reference;' @20  NULL (0 e84d0777)
 - private transient strict 'discovered' 'Ljava/lang/ref/Reference;' @24  a 'java/lang/ref/PhantomReference'{0x0000000742683bb8} (e84d0777 0)

這裏的 PhantomReference 爲 我們爲 我們創建的 Obj 實例 o 創建的 PhantomReference 

 

3.4 process_discovered_reflist(_discoveredPhantomRefs 處理之後

打印 _discoveredPhantomRefs 列表的數據如下 

## 省略掉部分無關的 CleanerImpl$PhantomCleanableRef, Cleaner
# 找不到 0x0000000742683bb8 對應的 PhantomReference 了

這裏我們發現 Obj 的實例 o 對應的  PhantomReference ref 被從 discoverList 裏面移除了 

這個是因爲 上面處理 o 對應的 FinalReference 的時候, 將 o 複製到了 存活區, 因此 這裏在 o 對應的 PhantomReference 處理 process_phase2 的時候, 因爲 PhantomReference 還存活, 所以將 PhantomReference ref 移出了 discoverList 

 

所以 在 第一個 System.gc 的時候 Obj 的實例 o 對應的 FinalReference 進入了 Finalizer.queue 

但是 Obj 的實例 o 對應的 PhantomReference ref 沒有進入其對應的 referenceQueue 

 

3.5 第二次 System.gc process_discovered_reflist(_discoveredFinalRefs 處理之前

打印 _discoveredFinalRefs 列表的數據如下 

java.lang.ref.Finalizer
{0x00000007422741d8} - klass: 'java/lang/ref/Finalizer'
 - ---- fields (total size 5 words):
 - private strict 'referent' 'Ljava/lang/Object;' @12  a 'java/lang/ClassLoader$NativeLibrary'{0x00000007426d3dd8} (e84da7bb e84487a3)
 - volatile strict 'queue' 'Ljava/lang/ref/ReferenceQueue;' @16  a 'java/lang/ref/ReferenceQueue'{0x0000000742243d18} (e84487a3 0)
 - volatile 'next' 'Ljava/lang/ref/Reference;' @20  NULL (0 e844b086)
 - private transient strict 'discovered' 'Ljava/lang/ref/Reference;' @24  a 'java/lang/ref/Finalizer'{0x0000000742258430} (e844b086 e84521bd)
 - private 'next' 'Ljava/lang/ref/Finalizer;' @28  a 'java/lang/ref/Finalizer'{0x0000000742290de8} (e84521bd e844b086)
 - private 'prev' 'Ljava/lang/ref/Finalizer;' @32  a 'java/lang/ref/Finalizer'{0x0000000742258430} (e844b086 0)
java.lang.ref.Finalizer
{0x0000000742258430} - klass: 'java/lang/ref/Finalizer'
 - ---- fields (total size 5 words):
 - private strict 'referent' 'Ljava/lang/Object;' @12  a 'java/lang/ClassLoader$NativeLibrary'{0x00000007426d3e00} (e84da7c0 e84487a3)
 - volatile strict 'queue' 'Ljava/lang/ref/ReferenceQueue;' @16  a 'java/lang/ref/ReferenceQueue'{0x0000000742243d18} (e84487a3 0)
 - volatile 'next' 'Ljava/lang/ref/Reference;' @20  NULL (0 e844b086)
 - private transient strict 'discovered' 'Ljava/lang/ref/Reference;' @24  a 'java/lang/ref/Finalizer'{0x0000000742258430} (e844b086 e844e83b)
 - private 'next' 'Ljava/lang/ref/Finalizer;' @28  a 'java/lang/ref/Finalizer'{0x00000007422741d8} (e844e83b 0)
 - private 'prev' 'Ljava/lang/ref/Finalizer;' @32  NULL (0 0)

沒有了 Obj 實例對應的 o 的 FinalReference, 沒有 強引用引用關聯 該 FinalReference 了, 複製算法 Discover 不到該 FinalReference 

 

3.6 第二次 System.gc process_discovered_reflist(_discoveredPhantomRefs 處理之前

打印 _discoveredPhantomRefs 列表的數據如下 

## 省略掉部分無關的 CleanerImpl$PhantomCleanableRef, Cleaner
# 找不到 0x0000000742683bb8 對應的 PhantomReference 了

沒有了 Obj 實例對應的 o 的 PhantomReference, 沒有 強引用引用關聯 該 PhantomReference了, 複製算法 Discover 不到該 PhantomReference

所以 第二次 gc 的時候, Obj 的實例 o 對應的 PhantomReference ref 沒有進入其對應的 referenceQueue  

 

經過這一次 gc, 一般情況下 Obj 實例 o, 對應的 FinalReference 已經處理了 finalize 的相關業務[sleep 3s], 並且 FinalizerThread 處理了該 FinalReference的後續流程, Obj 的實例 o, o 對應的 FinalReference, o 對應的 PhantomReference 都會被處理掉 

 

 

4. 如何確保 o 對應的 PhantomReference 入隊

相信看完了 上面的這一系列, 你應該有辦法了吧 

 

 

參考

https://hllvm-group.iteye.com/group/topic/34934

https://blog.csdn.net/KevinMite/article/details/83745080 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章