Java可执行命令详解之jdb
- 1️⃣ 概念
- 2️⃣ 优势和缺点
- 3️⃣ 使用
- 3.1 语法格式
- 3.1.1 参数:-sourcepath < path>
- 3.1.2 指令:run [class [args]]
- 3.1.3 指令:print < expr>
- 3.1.4 指令:stop at< class>:< line>
- 3.1.5 指令:watch [access|all] < class id>.< field name>
- 3.1.6 指令:step
- 4️⃣ 应用场景
- 5️⃣ 注意事项
- 6️⃣ 扩展:现在流行的调试工具
- 🌾 总结
1️⃣ 概念
jdb
是Java开发工具中的一部分,是一个用于调试Java程序的命令行工具。它由Sun Microsystems (现在是Oracle Corporation) 开发,并随JDK(Java Development Kit)一同提供。jdb
旨在提供一个交互式的调试环境,以帮助开发者诊断和修复Java应用程序中的错误。
jdb
提供了一个类似于传统命令行调试器的界面,可以在运行 Java 程序时暂停程序的执行,并允许开发者使用各种调试功能,例如设置断点、查看变量和堆栈信息、执行单步调试、监视变量值等。
其主要作用是帮助开发者定位和解决Java程序中的错误和异常。通过使用 jdb
调试器,开发者可以逐行检查代码、观察变量的值和状态,并在运行时动态地修改和测试代码逻辑。
jdb
是通过使用 Java 虚拟机自身提供的调试接口(JVMTI
)来实现的。JVMTI 允许外部工具与正在运行的 Java 程序进行交互,并提供了访问程序状态和控制程序执行的能力,这使得 jdb
可以在程序运行时获取和修改程序的状态。
2️⃣ 优势和缺点
优点:
- 强大的调试功能:
jdb
提供了丰富的调试功能,使得开发者能够更深入地理解和追踪程序的执行过程; - 命令行界面:命令行界面提供了更快速和高效的调试体验,并且可以在不同的操作系统上使用。
缺点:
- 学习曲线较陡峭:与一些集成开发环境(IDE)相比,
jdb
的命令行界面可能对于初学者来说有一定的学习曲线; - 依赖控制台:由于
jdb
是一个命令行工具,它需要运行在支持终端或命令行界面的操作系统上。它可能不适用于所有的场景,特别是在没有命令行界面访问权限的远程服务器上。
3️⃣ 使用
3.1 语法格式
jdb
的使用语法如下所示:
jdb [options] <class> [arguments]
其中:
options
:可选的调试器参数;<class>
:要调试的Java类的名称;arguments
:传递给主程序的参数。
jdb
命令支持一些可选参数来控制调试过程。汇总全部的可选参数如下表:
参数 | 作用 |
---|---|
-sourcepath <由 ";"分隔的目录> | 要在其中查找源文件的目录 |
-attach <address> | 使用标准连接器附加到指定地址处正在运行的 VM |
-listen <address> | 等待正在运行的 VM 使用标准连接器在指定地址处连接 |
-listenany | 等待正在运行的 VM 使用标准连接器在任何可用地址处连接 |
-launch | 立即启动 VM 而不是等待 ‘run’ 命令 |
-listconnectors | 列出此 VM 中的可用连接器 |
-connect <connector-name>:<name1>=<value1>,... | 使用所列参数值通过指定的连接器连接到目标 VM |
-dbgtrace [flags] | 输出信息供调试jdb |
-tclient | 在 HotSpot™ 客户机编译器中运行应用程序 |
-tserver | 在 HotSpot™ 服务器编译器中运行应用程序 |
使用jdb
命令和参数之后,就会进入调试行,此时就可以开始调试过程。而在调试行可操作的命令汇总如下表:
指令 | 作用 |
---|---|
connectors | 列出此 VM 中可用的连接器和传输 |
run [class [args]] | 开始执行应用程序的主类 |
- | - |
threads [threadgroup] | 列出线程 |
thread <thread id> | 设置默认线程 |
suspend [thread id(s)] | 挂起线程 (默认值: all) |
resume [thread id(s)] | 恢复线程 (默认值: all) |
where [<thread id> | all] | 转储线程的堆栈 |
wherei [<thread id> | all] | 转储线程的堆栈, 以及 pc 信息 |
up [n frames] | 上移线程的堆栈 |
down [n frames] | 下移线程的堆栈 |
kill <thread id> <expr> | 终止具有给定的异常错误对象的线程 |
interrupt <thread id> | 中断线程 |
- | - |
print <expr> | 输出表达式的值 |
dump <expr> | 输出所有对象信息 |
eval <expr> | 对表达式求值 (与 print 相同) |
set <lvalue> = <expr> | 向字段/变量/数组元素分配新值 |
locals | 输出当前堆栈帧中的所有本地变量 |
- | - |
classes | 列出当前已知的类 |
class <class id> | 显示已命名类的详细资料 |
methods <class id> | 列出类的方法 |
fields <class id> | 列出类的字段 |
- | - |
threadgroups | 列出线程组 |
threadgroup <name> | 设置当前线程组 |
- | - |
stop in <class id>.<method>[(argument_type,...)] | 在方法中设置断点 |
stop at <class id>:<line> | 在行中设置断点 |
clear <class id>.<method>[(argument_type,...)] | 清除方法中的断点 |
clear <class id>:<line> | 清除行中的断点 |
clear | 列出断点 |
catch [uncaught|caught|all] <class id>|<class pattern> | 出现指定的异常错误时中断 |
ignore [uncaught|caught|all] <class id>|<class pattern> | 对于指定的异常错误, 取消 ‘catch’ |
watch [access|all] <class id>.<field name> | 监视对字段的访问/修改 |
unwatch [access|all] <class id>.<field name> | 停止监视对字段的访问/修改 |
trace [go] methods [thread] | 跟踪方法进入和退出。除非指定 ‘go’, 否则挂起所有线程 |
trace [go] method exit | exits [thread] | 跟踪当前方法的退出, 或者所有方法的退出。除非指定 ‘go’, 否则挂起所有线程 |
untrace [methods] | 停止跟踪方法进入和/或退出 |
step | 执行当前行 |
step up | 一直执行, 直到当前方法返回到其调用方 |
stepi | 执行当前指令下一步。步进一行 (步过调用) |
cont | 从断点处继续执行 |
- | - |
list [line number|method] | 输出源代码 |
use (或 sourcepath) [source file path] | 显示或更改源路径 |
exclude [<class pattern>, ... | "none"] | 对于指定的类, 不报告步骤或方法事件 |
classpath | 从目标 VM 输出类路径信息 |
- | - |
monitor <command> | 每次程序停止时执行命令 |
monitor | 列出监视器 |
unmonitor <monitor#> | 删除监视器 |
read <filename> | 读取并执行命令文件 |
- | - |
lock <expr> | 输出对象的锁信息 |
threadlocks [thread id] | 输出线程的锁信息 |
- | - |
pop | 通过当前帧出栈, 且包含当前帧 |
reenter | 与 pop 相同, 但重新进入当前帧 |
redefine <class id> <class file name> | 重新定义类的代码 |
- | - |
disablegc <expr> | 禁止对象的垃圾收集 |
enablegc <expr> | 允许对象的垃圾收集 |
- | - |
!! | 重复执行最后一个命令 |
<n> <command> | 将命令重复执行 n 次 |
# <command> | 放弃 (无操作) |
help (或 ?) | 列出命令 |
version | 输出版本信息 |
exit (或 quit) | 退出调试器 |
上面表格汇总了jdb
的调试模式中,所有一共近60个调试命令,读者可以根据自己的需求参照表格选择所需指令。下是主要介绍一些常用的 jdb
可选参数或指令:
-sourcepath <path>
:指定源代码路径;run [class [args]]
:开始执行应用程序的主类;print <expr>
:打印表达式的值;stop at<class>:<line>
:在指定的源代码位置设置断点;watch [access|all] <class id>.<field name>
:监视指定变量的值;step
:执行单步调试。
3.1.1 参数:-sourcepath < path>
jdb -sourcepath <path>
命令用于指定源代码的路径,这对于在调试过程中查看源代码非常有用。以下是一个演示:
假设有一个名为 MyApp
的Java应用程序,并且源代码位于 /path/to/source
目录下。我们希望在调试期间能够访问到正确的源代码。在这种情况下,我们可以使用以下命令来启动 jdb
并设置源代码路径:
jdb -sourcepath /path/to/source MyApp
运行该命令后,jdb
将启动并等待连接到 MyApp
应用程序进程。此时,如果在应用程序中设置了断点,jdb
将暂停应用程序的执行,并允许您逐步调试代码。使用 -sourcepath
选项后,可以使用 list
命令查看当前断点所在位置的源代码,从而更容易地理解正在调试的代码。
3.1.2 指令:run [class [args]]
当使用 jdb
的 run [class [args]]
指令时,可以运行 Java 应用程序进行调试。以下是一个案例演示:
使用 jdb
运行应用程序,可按照以下步骤操作:
-
在命令行中输入命令来启动
jdb
调试器:jdb
-
在
jdb
命令提示符下,使用run
命令并指定要运行的类名和参数(可选):run MyApp
如果应用程序需要命令行参数,可以在
run
命令后添加它们:run MyApp arg1 arg2
-
jdb
将尝试加载并运行指定的类。如果成功,它将开始执行应用程序。 -
应用程序开始执行时,
jdb
将以调试模式暂停应用程序的执行,并返回到jdb
的命令提示符。这意味着我们可以开始在代码中设置断点、查看变量值,并进行其他调试操作。
需要注意确保已经编译并生成了可调试的 Java 类文件,才能进行运行和调试。
3.1.3 指令:print < expr>
当使用 jdb
的 print <expr>
指令时,可以在调试过程中打印表达式的值。以下是一个案例演示:
假设正在调试一个 Java 应用程序,下面是一个简单的 Java 类:
public class MyApp {
public static void main(String[] args) {
int x = 10;
int y = 5;
int sum = x + y;
System.out.println("Sum: " + sum);
System.out.println("Product: " + multiply(x, y));
}
public static int multiply(int a, int b) {
return a * b;
}
}
现在,我们在 jdb
中设置断点,并使用 print
命令来查看变量的值:
-
在命令行中输入以下命令来启动
jdb
调试器并指定要调试的类:jdb MyApp
-
使用
stop at
命令设置断点,例如在multiply
方法内的第一行:stop at MyApp.multiply:3
-
使用
run
命令启动应用程序:run
-
当应用程序执行到断点处时,
jdb
将暂停应用程序的执行。 -
现在,可以使用
print
命令来打印表达式的值。例如,要打印变量x
和y
的值:print x print y
jdb
将在命令行中显示变量的值。 -
还可以在
print
命令中使用表达式。例如,要打印x
和y
的和以及调用multiply
方法的返回值:print x + y print multiply(x, y)
jdb
将计算并打印表达式的结果。 -
在完成调试操作后,可以使用
cont
命令继续执行应用程序。
这个案例演示了如何在 jdb
中使用 print
命令来查看变量和表达式的值。注意,在每个断点处,变量的作用域是可见的。
3.1.4 指令:stop at< class>:< line>
当使用 jdb
的 stop at <class>:<line>
指令时,可以在指定的类和行号上设置断点。以下是一个案例演示:
同样使用上边案例的 MyApp
Java类来演示在特定类和行号上设置断点以进行调试。
-
在命令行中输入以下命令来启动
jdb
调试器并指定要调试的类:jdb MyApp
-
在
jdb
提示符下,使用stop at
命令来设置断点。例如,设置在main
方法内的第 6 行处:stop at MyApp:6
这将在指定的类和行号上设置一个断点。
-
使用
run
命令启动应用程序:run
应用程序将开始执行,并在达到断点位置时暂停。
-
当应用程序暂停时,可以查看变量的值、执行其他调试操作以及逐行调试。
注意,jdb
默认情况下会在应用程序开始执行后立即停止。因此需要在应用程序达到断点之前设置断点。
3.1.5 指令:watch [access|all] < class id>.< field name>
当使用 jdb
的 watch [access|all] <class id>.<field name>
指令时,可以设置监视点以在字段访问时触发暂停。以下是一个案例演示:
假设有一个 MyClass
的Java类,其中包含一个实例字段 count
,现在希望在每次修改 count
字段值时暂停并进行调试。下面是一个简单的示例:
public class MyClass {
private int count = 0;
public void increment() {
count++;
}
public int getCount() {
return count;
}
public static void main(String[] args) {
MyClass obj = new MyClass();
obj.increment();
System.out.println("Count: " + obj.getCount());
}
}
现在,让我们在 jdb
中设置监视点并进行调试:
-
在命令行中输入以下命令来启动
jdb
调试器并指定要调试的类:jdb MyClass
-
在
jdb
提示符下,使用stop in
命令设置断点,例如在increment
方法内的第一行:stop in MyClass.increment:1
-
使用
run
命令启动应用程序:run
应用程序将开始执行,并在达到断点位置时暂停。
-
当应用程序暂停时,可以使用
watch
命令设置监视点。例如,设置对count
字段的访问进行监视:watch access MyClass.count
此命令将在字段访问时触发暂停。
如果希望在字段读取或写入时都触发暂停,可以使用
watch all
命令:watch all MyClass.count
-
在监视点设置后,当应用程序访问或修改
count
字段时,jdb
将暂停,并返回到jdb
的命令提示符以供进一步调试。
需要注意监视点只能设置在可调试的字段上,并且只在第一个访问或修改操作发生时触发暂停。
3.1.6 指令:step
当使用 jdb
的 step
指令时,可以逐语句执行代码并进入方法调用。以下是一个案例演示:
现在有一个 MyClass
的Java类,其中包含一个方法调用链,我们希望在每个方法调用和逐语句执行代码时进行调试。下面是一个简单的示例:
public class MyClass {
public void methodA() {
System.out.println("Inside methodA");
methodB();
}
public void methodB() {
System.out.println("Inside methodB");
methodC();
}
public void methodC() {
System.out.println("Inside methodC");
}
public static void main(String[] args) {
MyClass obj = new MyClass();
obj.methodA();
}
}
-
在命令行中输入以下命令来启动
jdb
调试器并指定要调试的类:jdb MyClass
-
在
jdb
提示符下,使用stop in
命令设置断点,例如,在methodA
方法内的第一行:stop in MyClass.methodA:1
-
使用
run
命令启动应用程序:run
应用程序将开始执行,并在达到断点位置时暂停。
-
当应用程序暂停时,您可以使用
step
命令逐语句执行代码。键入以下命令并按回车键:step
这将执行当前行,并逐语句地执行代码。
-
可以重复使用
step
命令,每次执行一行代码并进入方法调用,直到代码执行完成或到达断点。
step
命令会进入所有方法调用,因此可能需要多次执行该命令来查看每个方法的执行。
4️⃣ 应用场景
jdb
在以下情况下特别有用:
- 错误排查:当程序出现错误、崩溃或异常时,可以使用
jdb
来分析问题并找到修复代码的位置; - 代码调试:可以在程序执行的不同位置设置断点,逐行调试以便观察代码行为和验证逻辑;
- 性能调优:可以使用
jdb
来检测程序中的性能瓶颈,分析 CPU 和内存使用情况,并找到优化的机会。
5️⃣ 注意事项
在使用 jdb
进行调试时,请注意以下几点:
- 确保使用
-g
选项对源代码进行编译,以便在调试器中能够正确地查看变量和源代码; - 仔细阅读
jdb
的文档,并了解每个命令和选项的作用; - 使用合适的断点,以避免存在太多或不必要的断点;
- 在调试期间,观察变量的值及其对程序行为的影响;
- 避免在生产环境中使用
jdb
调试,应将其限制在开发和测试环境中使用。
6️⃣ 扩展:现在流行的调试工具
在Java开发领域,有许多流行的代码调试工具可供选择,尤其是现在的IDE
软件基本都集成了更加直观的图形化调试工具,这也使得本文介绍的传统的命令行调试方式,因其比较繁多及复杂的指令格式逐渐被大众开发者所遗忘或摒弃。以下是一些目前广泛使用的Java代码调试工具:
- IntelliJ IDEA:IntelliJ IDEA是一个很流行的Java IDE,也提供了高级的调试功能。它支持断点调试、条件断点、表达式求值和远程调试等功能,并具有用户友好的界面;
- Eclipse:Eclipse是一个强大的Java集成开发环境(IDE),具有内置的调试功能。它提供了逐行调试、断点设置、变量监视和堆栈跟踪等实用特性;
- NetBeans:NetBeans是一款免费的开源Java IDE,内置了强大的调试功能。它支持基本调试操作,如断点设置、单步执行和变量监视,同时还提供了高级功能,如异常捕获和线程调试;
- jdb:在本文我们已经详细介绍了
jdb
,这是Java自带的命令行调试器,适用于那些更喜欢命令行界面和脚本化操作的开发者。
这些是当前流行的Java代码调试工具,每个工具都有其特定的优点,并适用于不同类型的项目和开发需求。选择适合自己的工具取决于个人偏好、项目要求和团队协作等因素。建议根据自身的需求,尝试并选择最适合您的Java代码调试工具。
🌾 总结
jdb
是一个功能强大且灵活的 Java 调试器,提供了一系列功能和选项来帮助开发者定位、调试和修复 Java 应用程序中的问题。通过设置断点、单步调试、监视变量等操作,开发者可以深入了解程序的执行状态,并进行错误排查和性能调优。
尽管 jdb
的命令行界面对于新手而言可能有一定的学习曲线,但通过熟悉其语法和常用选项,开发者可以更加高效地利用 jdb
进行调试工作。同时,需要注意在合适的情况下使用 jdb
,并遵循最佳实践以确保有效地使用调试器工具。