Java java.lang.ref.Finalizer 上设置的断点未停止

Java Breakpoint set on java.lang.ref.Finalizer not stopped

我想深入了解 Java java.lang.ref.Finalizer 初始化过程,所以我在其 class 静态块上设置了一个断点:

static {
->    ThreadGroup tg = Thread.currentThread().getThreadGroup(); // breakpoint set on this line
    for (ThreadGroup tgn = tg;
         tgn != null;
         tg = tgn, tgn = tg.getParent());
    Thread finalizer = new FinalizerThread(tg);
    finalizer.setPriority(Thread.MAX_PRIORITY - 2);
    finalizer.setDaemon(true);
    finalizer.start();
}

然后通过 IntelliJ IDEA 调试按钮启动一个空的 main() 方法,但断点不会停止程序(JVM 只是执行到它的末尾并退出。)

Java版本:

openjdk version "1.8.0_302"
OpenJDK Runtime Environment Corretto-8.302.08.1 (build 1.8.0_302-b08)
OpenJDK 64-Bit Server VM Corretto-8.302.08.1 (build 25.302-b08, mixed mode)

为什么这个断点没有生效?

从JDK5.0开始,JVM调试能力基于JVM TI构建,取代了JVMPI和JVMDI。我不熟悉 JVMDI,因此以下陈述是基于您使用 agentlib:jdwp 调试代码的事实,例如:

-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=5005

jdwp 代理(从 libjdwp.so 文件加载)在接收到 post_vm_initialized 事件时绑定指定的端口(上例中的 5005)。正如事件名称所示,VM 已经初始化并且 Finalizer class 已经在调试端口绑定发生时加载 (当然你只能调试 Java JDWP端口监听后的代码)。所以你不能调试Finalizer的静态块代码。事实上,在JDWP端口绑定之前加载的classes在初始化方面都是不可调试的。但是如果线程运行在loaded-before class之后的代码中,你仍然可以调试它,例如Finalizer.FinalizerThread#run方法。

我从 The JVM Tool Interface (JVM TI): How VM Agents Work 中找到了一句话:

The library is loaded before the VM has initialized, allowing the agent library to capture early VM events that it could not access before.

JDWP agentlib 另一方面可以在 Agent_Onload 函数中绑定端口(在 debugInit.c 中),这可能允许在 JVM 中加载所有 classes 的调试(我猜).

PS:如果您对上述代码感兴趣(在 OpenJDK 8 中,Mercurial ID dcb218f54ca1):

  1. post_vm_initialized 函数中的事件处理 debugInit.c:initialize.
  2. JDWP 的 Agent_Onload 函数也位于代码文件 debugInit.c.