Debian上Java编译与运行阶段的实用调试技巧
一 环境确认与版本控制
- 安装并验证开发工具链:确保已安装JDK(而非仅JRE),执行sudo apt update && sudo apt install openjdk-11-jdk,随后用java -version与javac -version确认版本一致。若系统存在多版本,使用sudo update-alternatives --config java切换默认版本,避免“编译与运行版本不一致”的隐蔽问题。
- 正确设置JAVA_HOME与PATH:编辑**/etc/environment**(全局)或**~/.bashrc**(用户级),例如:JAVA_HOME=“/usr/lib/jvm/java-11-openjdk-amd64”,并在PATH中加入**$JAVA_HOME/bin**;修改后执行source使其生效。
- 快速自检清单:
- 执行which java与which javac确认命令解析路径;
- 执行echo $JAVA_HOME与echo $PATH核对环境变量;
- 若使用构建工具(如Maven/Gradle),确认其使用的JDK与命令行一致(可设置JAVA_HOME或在工具配置中显式指定)。
二 编译期问题定位
- 最小化复现与清理:删除旧产物(如*rm .class)后再编译,避免增量编译残留导致“明明已改仍报错”。
- 显式声明依赖与源码路径:使用**-cp/-classpath指定依赖JAR,使用-sourcepath与-d**分离源码与输出目录,例如:
- 编译:javac -sourcepath src -d bin src/com/example/Main.java
- 运行:java -cp bin:lib/dep.jar com.example.Main
- 读懂编译器输出:关注报错的文件、行号、符号未找到(多半是依赖未加入classpath或版本不匹配)、语法/泛型错误等关键信息;必要时将错误输出重定向到文件便于检索。
- 保持工具链更新:执行sudo apt update && sudo apt upgrade,修复可能的工具链/依赖冲突。
- 多模块/多版本工程的常见坑:确认module-path(Java 9+)或classpath是否覆盖所有模块;确认target/source兼容性与编译器参数一致性。
三 运行期与JVM层诊断
- 远程调试(JDWP):在应用启动参数中加入
- java -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005 -jar app.jar
在IDE(如IntelliJ IDEA)创建“Remote JVM Debug”配置,Host填localhost、Port填5005;如需远程调试,请放行防火墙端口。
- 线程与死锁排查:
- 用jps获取进程ID;
- 用**jstack **导出线程栈,结合关键字“waiting to lock”“locked”定位竞争与死锁;
- 将多次jstack输出对比,识别长期RUNNABLE或BLOCKED的热点线程。
- 内存与元空间问题:
- 生成堆转储:jmap -dump:format=b,file=heap.hprof ;用Eclipse MAT或VisualVM分析Dominator Tree与引用链;
- 检查元空间:jmap -clstats 观察类加载器与类数量,排查类加载器泄漏。
- 容器与复杂场景:在Tomcat等容器中,优先查看**/var/log/tomcatX/下的日志,必要时开启并分析GC日志**(-Xloggc),配合线程转储定位瓶颈。
四 进阶与底层分析
- 使用gdb抓取JVM原生栈(仅在必要时):
- 安装:sudo apt install gdb;
- 附加:gdb -p <java_pid>;
- 打印所有线程原生栈:thread apply all backtrace;
- 适用于JVM崩溃、挂起或原生库问题的现场分析。
- 结合Arthas在线诊断:在不改代码的前提下反编译、观察方法耗时、定位热方法,必要时配合火焰图与编译日志分析JIT/C2相关性能问题。
五 高效排查的推荐流程
- 复现与最小化:用最小源码与依赖重现问题,便于定位是“代码问题”还是“环境问题”。
- 环境核对:java/javac版本一致、JAVA_HOME/PATH正确、依赖在classpath中。
- 编译期:清理后重编译,显式声明-sourcepath/-cp/-d,阅读编译器报错行号与符号。
- 运行期:先用jps+jstack定位线程/锁,再用jmap+MAT分析内存与类加载,必要时远程调试进入断点。
- 仍无法定位:开启GC日志、采集线程/堆转储,结合gdb/Arthas深入分析,并保留完整日志以便复盘。