配置openjdk调试环境
先决条件
首先在Ubuntu 18.04上编译SlowDebug版本的openjdk。注意,这里我选择的是x86处理器的电脑。苹果M系列属于ARM芯片,指令集不一样。由于我在苹果上进行垃圾回收调试的时候会报SIGILL错误。为了了解JVM的内部工作原理,不要在这种问题上过多纠结,另外,x86的资源也更丰富一些,因此,我直接选择x86处理器进行调试。
我机器的处理器为AMD Ryzen 7 8845HS w/ Radeon 780M Graphics 3.80 GHz
为了减少寻找在何处调试的时间,这里告诉大家,直接定位到
openjdk/build/linux-x86_64-normal-server-slowdebug/jdk/bin
这里面就是JVM虚拟机可执行文件的入口。例如,平时大家执行的
java -version
就是调用这里的java可执行文件。如果你进入该文件夹,并执行当前文件夹下的
./java -version
能够正确显示版本号,则证明编译成功了。
工具选择
如果使用gcc和g++编译的openjdk,那么就选择gdb进行调试。前面已经说了,目标jdk是openjdk11,操作系统为Ubuntu 18.04,这里直接用apt install
安装gdb
即可进行调试。
为什么选用gdb而不是图形界面调试
为此问题我花了1个月,一直尝试看能否用IDE的调试工具进行调试,然而结果不尽人意。我想基于gdb调试主要有2个原因。
- JVM自身的开发调试环境
JVM的初衷是在Linux下运行的虚拟机,甚至想独立于操作系统,但是目前看起来没有办法完全脱离操作系统运行,而且其内部实现也调用了系统库。如果是在Linux下运行,那么用命令行进行调试就能解决几乎全部的问题。
- 图形界面调试也是基于命令行
图形界面调试工具,例如,visual studio code,实际上是通过配置文件选择使用何种调试器,只不过将调试器原本应该通过终端显示的内容转换到IDE中显示。但这里实际上是绕了一个弯子,有些调试还不能很好的支持,比如对汇编语言的调试,而这正是JVM中需要考虑的部分。
- 命令调试可100%定位函数
目前JVM源码中main函数有多处,我如何知道哪里是启动main函数?
以往,对于C#或者Java程序,我们都是通过打开已知的文件,比如在C#中的program.cs文件,找到main函数,以确定函数入口,但是对于JVM这种C++编写的大型软件,看起来只能用命令行的gdb来确定入口main函数了。比如:
/* main.c */
#include <stdio.h>int main(){printf("Hello world!");return 0;
}
通过gcc编译
gcc -g main.c -o main
使用gdb调试上述C程序时,只需要指定要在哪个函数断开,不需要特别指定文件。
gdb ./main
b main
r
如果调试JVM,同样也只需要执行b main
就会自动寻找在哪个main函数上停止。
如果直接在生成的文件夹下执行gdb ./java会报错
在openjdk/build/linux-x86_64-normal-server-slowdebug/jdk/bin
下执行
gdb -command=.gdbinit ./java
然后设置断点
b main
然后运行
r
此时,JVM就停止在了main函数处。至少,我们已经能够进入JVM了。
如果在visual studio code中,你还可以将鼠标放在终端的文件名上,然后按住ctrl+鼠标右键即可直接导航到文件处。 MacOS是command+鼠标右键进行导航