调试器是软件开发领域最重要的工具之一,对Java开发来说,在开发环境可以使用Intellij IDEA进行debug从而快速解决问题,但在部署环境由于无法debug 经常需要耗时费力地添加打印语句并替换部署包。实际上JDK已经提供了一个命令行debug工具jdb,可以在任何环境使用它实现快速有效地问题定位。
2. 用法
jdk的bin目录下包含了jdb,jdb命令的基础语法是 jdb [options] [classname] [arguments]
,其中 options
是命令的选项,classname
和 arguments
是要调试的main类名和main方法参数。
可以注意到jdb命令的 classname
和 arguments
都使用了中括号,表示它们都不是必须的,因为jdb还可以attache到一个已经在运行的JVM上,方法与普通的 Remote Debug
相同,调试Spring Boot应用时使用这种方法更方便。
#启动时添加debug参数
java -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005 -jar app.jar
#attach到已经启动的JVM上
jdb -attach 5005
#与attach效果相同,attach失败时可以使用此命令
jdb -connect com.sun.jdi.SocketAttach:hostname=127.0.0.1,port=5005
jdb执行成功后会进入到一个交互式shell页面中,后续的调试操作都在此shell中进行,具体调试方法参考示例章节
4. 示例
本节演示如下比较常用的调试命令
命令 | 描述 |
---|---|
|
在方法中设置断点 |
|
在行中设置断点 |
|
查看所有断点 |
|
清除方法中的断点 |
|
清除行中的断点 |
|
执行当前行 |
|
一直执行, 直到当前方法返回到其调用方 |
|
执行当前指令 |
|
步进一行 (步过调用) |
|
从断点处继续执行 |
|
输出当前堆栈帧中的所有本地变量 |
|
输出表达式的值 |
|
输出所有对象信息 |
|
对表达式求值 (与 print 相同) |
|
输出源代码 |
|
列出类的方法 |
|
列出类的字段 |
|
重复执行最后一个命令 |
|
将命令重复执行 n 次 |
4.1. 创建项目
创建一个Spring Boot项目添加 starter-web
依赖,并添加如下Controller
package io.github.c;
@RestController
public class A {
private int counter = 0;
@GetMapping("/s")
public String s() {
counter++;
System.out.println("counter = " + counter);
return a();
}
public String a() {
String b = b();
for(int i = 0; i < 3; i++) {
b += i;
}
return b;
}
public String b() {
String s = "b";
s += "d";
return s;
}
}
4.2. 启动项目
项目打包后使用如下命令启动
java -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005 -jar app.jar
在另一个shell中使用jdb命令attach到启动的JVM上,注意命令末尾使用 -sourcepath
参数指定了项目的源码位置,这样在调试时可以查看对应的源码
jdb -connect com.sun.jdi.SocketAttach:hostname=127.0.0.1,port=5005 -sourcepath D:\ideaprojects\boottest\src\main\java