我想了解一些Javajava.lang.ref.Finalizer
初始化过程,所以我在它的类静态块上设置了一个断点:
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)
为什么这个断点不生效?
从JDK 5.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
类已经加载(当然,您只能在JDWP端口侦听之后调试Java代码)。因此,您无法调试Finalizer
的静态块代码。实际上,在JDWP端口绑定之前加载的类在初始化方面都是不可调试的。但是,如果线程在之后加载之前的类的代码中运行,您仍然可以调试它,例如Finalizer.FinalizerThread#run
方法。
我找到了JVM工具接口(JVM TI)的引用:VM代理如何工作:
在VM初始化之前加载库,允许代理库捕获以前无法访问的早期VM事件。
JDWP agentlib可以在Agent_Onload
函数中绑定端口(在debugInit.c
中),这可能允许在JVM中加载所有类的调试(我猜)。
PS:如果您对上面提到的代码感兴趣(在OpenJDK 8中,Mercurial ID为dcb218f54ca1):
post_vm_initialized
事件处理函数debugInit.c:initialize
.- JDWP的
Agent_Onload
函数也位于代码文件debugInit.c
。