性能文章>01 PhantomReference没有进入ReferenceQueue>

01 PhantomReference没有进入ReferenceQueue原创

10月前
160923

前言 

最近 看到一篇文章, 然后 基于改文章的代码, 做了一些 调整, 然后 发现了一些 奇怪的事情, 并稍微整理了一下 原因

该文章地址 : 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 <970655147@qq.com>
 * @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 <970655147@qq.com>
   * @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 

 

点赞收藏
黄金键盘
请先登录,查看2条精彩评论吧
快去登录吧,你将获得
  • 浏览更多精彩评论
  • 和开发者讨论交流,共同进步

为你推荐

一次 GDB 源码角度分析 jvm 无响应问题

一次 GDB 源码角度分析 jvm 无响应问题

3
2