JAVA 远程调试方法小记

某些情况下我们可能需要排查生产环境或其他远程环境出现问题的原因,但本地难以复现,如果能像在本地调试一样调试远程环境,那么会大大提高解决问题的效率。此时就可以借助 JVM 远程调试功能帮助我们排查问题。

远程调试的步骤

  • 在远程服务中配置远程调试参数
  • 在远程服务器中放开远程调试端口并重启服务
  • 在客户端(本地)定义远程调试连接配置
  • 在客户端启动远程调试

配置远程调试参数

想要启动远程调试,需要现在远程应用程序上配置远程调试参数,不同的应用启动方式决定了不同的调试参数配置,常见的方式是程序运行在 Tomcat 中和使用 java -jar 直接运行。下面列举了不同 JDK 版本的远程调试参数配置示例。

JDK 9 or later

1
-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:9999

JDK 5-8

1
-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=9999

JDK 1.4.x

1
-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=9999

JDK 1.3.x or earlier

1
2
-Xnoagent -Djava.compiler=NONE -Xdebug
-Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=9999

常用的 JDK 版本是 JDK8 以上,支持使用新的 JDWP(Java Debug Wire Protocol)协议进行远程调试,针对此配置给出各个参数解释:

  • transport=dt_socket 指定了使用基于套接字的传输。
  • server=y 表示 JVM 将作为服务器运行,等待调试器的连接。
  • suspend=n 表示 JVM 在调试器连接之前不会挂起应用程序的启动。如果你想在启动时立即进行调试,可以将其更改为 suspend=y
  • address=9999 指定了监听的远程调试端口,客户端连接此端口才能进行远程调试。

Tomcat 配置远程调试参数

在 Tomcat 配置远程调试参数可以修改 JAVA_OPTS 环境变量,但更合适的是配置 CATALINA_OPTS,后者将设置的值限定在 Tomcat 内而不会影响其他 JVM 进程。

不建议直接修改 catalina.sh ,而是应该在 bin 目录下创建 setenv.sh 文件并赋予可执行权限,这个文件是专门用于设置环境变量用的,在 Tomcat 启动时会自动执行,因为 catalina.sh 中有这么一段处理逻辑:

1
2
3
4
5
6
7
8
9
# Ensure that any user defined CLASSPATH variables are not used on startup,
# but allow them to be specified in setenv.sh, in rare case when it is needed.
CLASSPATH=

if [ -r "$CATALINA_BASE/bin/setenv.sh" ]; then
. "$CATALINA_BASE/bin/setenv.sh"
elif [ -r "$CATALINA_HOME/bin/setenv.sh" ]; then
. "$CATALINA_HOME/bin/setenv.sh"
fi

根据 JDK 版本的不同在 setenv.sh 中写入不同的远程调试参数,以 JDK 8 为例:

1
export CATALINA_OPTS="-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=9999"

随后重启 Tomcat 即可启动远程调试:

1
2
3
./startup.sh
or
./catalina.sh start

此时终端输出多了远程调试的参数打印:

1
2
3
......
Using CATALINA_OPTS: -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=9999
Tomcat started.

以 Jar 包方式运行时配置远程调试参数

在某些场景下我们直接使用 java -jar 的方式启动应用程序,此时想启动远程调试,只需要在启动参数中加入远程调试参数即可:

1
java -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=9999 -jar your-application.jar

以 Java Service Wrapper 包装的 JAVA 应用配置远程调试参数

Java Service Wrapper 是一个 JAVA 应用包装器,它允许 JAVA 应用作为服务或守护进程在不同平台上安装和控制,为管理 JAVA 应用提供了可靠的方式。它具备自动重启、配置系统服务和增强日志等功能。

在 Java Service Wrapper 中配置远程调试参数需要更改wrapper.conf 配置文件,在 wrapper.java.additional 处增加调试参数,示例如下:

1
2
# Java Additional Parameters
wrapper.java.additional.1=-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=9999

其中 wrapper.java.additional 后面的序号需要根据实际序号递增。随后重启 wrapper 应用即可。

IDEA 创建并连接远程调试

在 IDEA 中添加一个远程调试启动配置:

输入远程 IP 地址、调试端口并选择对应的 JDK 版本,随后在代码中打断点启动调试即可。

远程调试的注意事项

  • 远程调试需要服务端放开额外的端口,并且影响用户使用,在某些情况下可能不会被允许远程调试。
  • 远程调试需要确保远程环境和本地环境的代码是一致的,否则可能在本地打不了断点。
  • 远程调试完毕不要忘记关闭(恢复启动参数)并重启应用。

JAVA 远程调试方法小记
https://cui.cc/remote-jvm-debug/
作者
南山崔崔
发布于
2024年4月9日
许可协议