什么是JDB

JDB是一个简单的Java命令行调试器,包含在JDK中。

我们在本文中将会使用一台Ubuntu主机,我们可以在/usr/bin中找到JDB:

#cd /usr/bin

#ls | grep jdb

提示:如果你使用的是Windows,可以在Java目录下的bin目录中找到JDB,本文的示例主要在Ubuntu下完成,但即使在windows中相关的技术也基本是相同的。

简介

本文将会接合一个实例来理解如何用JDB命令调试Java程序,而不是直接去看JDB的用法。

以下是本文用作示例的代码:

文件名:Debug.java

编译生成的Class文件:Debug.class

安卓-5-使用JDB调试Java应用-JE2Se ' Blog

这段代码片段中,Debug类的main方法调用了该类中的其他两个方法,编译后执行,会产生以下输出,如图:

安卓-5-使用JDB调试Java应用-JE2Se ' Blog

我们使用了-g选项来编译程序,编译器会在类文件中生成一些调试信息。

运行JDB

要调试Java程序,我们需要一条JDB到JVM的通信信道,因为我们的Java程序实际上是运行在JVM(java虚拟机)中的

如下所示,有多种连接JDB和JVM的方法。

方法1

使用这个方法,我们直接使用JDB来加载类文件,JDB会自动创建一个JAVA虚拟机,并建立连接。

安卓-5-使用JDB调试Java应用-JE2Se ' Blog

图中的Debug代表编译后生成的类文件

方法2

使用这种方法,我们先使用以下命令启动一个Java虚拟机,Java虚拟机会监听54321端口。

java -Xdebug -Xrunjdwp:transport=dt_socket,server=y,address=54321 Debug

安卓-5-使用JDB调试Java应用-JE2Se ' Blog

然后使用如下命令启动JDB连接到JVM。

jdb -attach 54321

安卓-5-使用JDB调试Java应用-JE2Se ' Blog

这种方法也可以用于远程调试,在下一期中,我们会使用这种方法来远程调试Android应用。本文中会使用第一种方法。

开始调试

我们现在开始用第一种方法来调试示例程序,但我们需要执行一条run命令来让JDB启动Java虚拟机,如下图:

安卓-5-使用JDB调试Java应用-JE2Se ' Blog

图中显示,启动了Java虚拟机后,程序立刻执行完成并退出了。

为了中断程序执行以便手工单步调试,我们需要在程序运行之前设置断点。

我们可以用”stop in”命令在方法开始的地方设置断点,如下图:

安卓-5-使用JDB调试Java应用-JE2Se ' Blog

值得注意的是,设置断点的时候,除了指定类和方法名之外,还要指定参数类型。

我们已经在Debug类中的main方法上设置了断点。

现在我们就能运行程序来触发断点,这里一样使用之前提到的run命令。

安卓-5-使用JDB调试Java应用-JE2Se ' Blog

触发断点后,JDB会自动显示将要执行的下一行代码:

System.out.println(“We are in main method”);

可以使用“list“命令来查看当前的上下文代码

安卓-5-使用JDB调试Java应用-JE2Se ' Blog

使用“clear“命令查看设置的所有断点:

安卓-5-使用JDB调试Java应用-JE2Se ' Blog

如图,clear命令显示出来我们设置断点及位置

使用”threadgroups”命令查看所有的线程组。

安卓-5-使用JDB调试Java应用-JE2Se ' Blog

如图,当前有两个线程组:“system“和”main“

使用”threads”查看所有线程:

安卓-5-使用JDB调试Java应用-JE2Se ' Blog

如上图,我们当前的system线程组中有三个线程,而main线程组中有一个线程,这就是我们要调试的。

使用”classes”命令查看当前Java虚拟机所加载的类的信息:

安卓-5-使用JDB调试Java应用-JE2Se ' Blog

上图中显示了当前Java虚拟机所加载的类(为节省空间,截短了输出)

要查看特定类的更加详细的信息,可以使用以下命令:

>class <classname>

下图显示了Debug类的详细信息:

安卓-5-使用JDB调试Java应用-JE2Se ' Blog

同样,我们也能查看其它类的详细信息,例如下图就显示了 java.io.DatqaInputStream类的详细信息:

安卓-5-使用JDB调试Java应用-JE2Se ' Blog

使用”methods <classname>”命令查看所加载的方法:

安卓-5-使用JDB调试Java应用-JE2Se ' Blog

以上介绍了以下常用的重要命令,现在我们将深入程序的执行流程,看怎样用JDB来帮助我们调试程序。

我们可以使用”next”命令执行下一行代码:

安卓-5-使用JDB调试Java应用-JE2Se ' Blog

执行完当前代码后,JDB会自动显示下一行代码:调用test方法。

这里,我们再执行”next”命令后,会执行完test方法并中断到下一行代码:passCheck:

安卓-5-使用JDB调试Java应用-JE2Se ' Blog

现在,如果我们想进入passCheck方法进行调试,就应该使用”step”命令,而不再是”next”。

我重启了程序,在test方法处输入了step命令:

安卓-5-使用JDB调试Java应用-JE2Se ' Blog

现在,输入”next”命令继续运行下一条代码:

安卓-5-使用JDB调试Java应用-JE2Se ' Blog

现在,如果我们因为某些原因希望直接离开该方法,而不是运行余下的代码,我们可以使用”step up”命令。

安卓-5-使用JDB调试Java应用-JE2Se ' Blog

当前代码已经离开test方法,回到main方法,等待执行下一条。

下面几行会毕竟有趣,我们将会学到如何查看变量中储存的数据,在此之前,我们先介绍一天更有趣的命令:“where”。

“where”命令会打印显示当前的调用栈。我们先在main方法中运行该命令,然后在另一个方法中运行同样的命令:

安卓-5-使用JDB调试Java应用-JE2Se ' Blog

如图,当前程序正在main方法中,

现在使用”step”命令进入方法,并检查调用栈:

安卓-5-使用JDB调试Java应用-JE2Se ' Blog
安卓-5-使用JDB调试Java应用-JE2Se ' Blog

如上图,程序当前正在”Debug.passCheck”中运行,而”Debug.passCheck”又是被“Debug.main”调用的

假如我现在对passCheck这个方法比较干兴趣,想看看该方法的局部变量中有不有一些敏感信息,我们可以使用”locals”命令查看所有的局部变量。(如果程序没有使用-g选项编译,该命令无效).

安卓-5-使用JDB调试Java应用-JE2Se ' Blog

如图显示,该方法接收了一个main方法传入的密码变量,因为参数还没有被赋值给局部变量,所有上图只显示了参数而没有局部变量,我们先执行下一行代码再查看局部变量”password”。

可以使用”print”命令打印出指定变量的内容:

安卓-5-使用JDB调试Java应用-JE2Se ' Blog

总结

本文简介介绍了JDB,以及几个常用的JDB命令的用法。

相关链接

http://docs.oracle.com/javase/7/docs/technotes/tools/windows/jdb.html