Java 开发日常排错是每个 Java 工程师的“必修课”。无论是新手还是老手,都会频繁遇到各种异常、性能问题、环境配置错误等。下面为你整理一份 系统化、结构化、实战导向的《Java 开发日常排错指南》,包含:

  • 🧩 常见错误分类
  • 🔍 排错方法论
  • 🛠️ 工具推荐
  • 📋 典型案例 + 解决方案(表格形式)
  • 💡 预防建议

🎯 一、Java 日常排错方法论(四步法)

STEP 1:看错误 → 定位类型
STEP 2:查日志 → 找上下文
STEP 3:复现问题 → 最小化场景
STEP 4:修复验证 → 加监控/测试


🧰 二、必备排错工具清单

类型 工具 用途说明
日志分析 grep, less, tail -f, ELK, Kibana 快速搜索异常堆栈、关键字
调试工具 IntelliJ IDEA Debugger, Eclipse Debugger 断点调试、变量观察
内存分析 jmap, jstat, VisualVM, MAT 分析 OOM、内存泄漏
线程分析 jstack, Arthas thread, fastthread.io 查死锁、线程阻塞、CPU 飙高
性能监控 Arthas, JProfiler, YourKit, Prometheus + Grafana 方法耗时、GC 情况、调用链
反编译 JD-GUI, CFR, Arthas jad 查看线上/第三方 jar 包源码
热修复/诊断 Arthas(阿里开源) 不重启 JVM,在线 watch/trace/redefine 类

💡 强烈推荐掌握 Arthas —— Java 线上诊断神器!


📊 三、Java 开发常见错误排错表(含解决方案)

✅ 可打印 / 导入 Excel / 作为团队知识库

错误现象 / 异常信息 可能原因 排错步骤 解决方案 预防措施
启动报错:ClassNotFoundException 类路径缺失、包名错误、依赖未引入 1. 检查 target/classes 是否有该类
2. 检查 pom.xml / build.gradle 依赖
3. 检查是否混淆或打包遗漏
添加依赖 / 修正包名 / 重新 clean install 使用 Maven/Gradle 依赖管理,避免手动拷 jar
运行时报:NoClassDefFoundError 类在编译时存在,运行时缺失(如 jar 冲突、未打入 fat jar) 1. java -verbose:class 查看类加载
2. mvn dependency:tree 检查冲突
3. 检查 fat jar 是否包含所需类
排除冲突依赖 / 使用 shade 插件合并 / 检查 Class-Path 构建时检查依赖树,使用 maven-shade-plugin
NullPointerException (NPE) 对象为 null 却调用方法/属性 1. 看堆栈定位行号
2. 检查上游赋值逻辑
3. 使用 IDEA “Analyze Data Flow”
判空处理 / 使用 Optional / 防御性编程 启用 SonarQube NPE 规则 / 使用 @NonNull 注解
OutOfMemoryError: Java heap space 内存泄漏、大对象、缓存无上限 1. jmap -histo:live <pid> 查看对象分布
2. 用 MAT 分析 dump 文件
3. Arthas watch 监控大对象创建
增加 -Xmx / 优化缓存淘汰策略 / 修复泄漏点 设置 JVM 监控告警 / 定期压测 / 使用弱引用
OutOfMemoryError: Metaspace 动态生成类过多(如 CGLIB、反射、Lambda) 1. jstat -gcmetacapacity <pid>
2. 检查是否有类加载器泄漏
增加 -XX:MaxMetaspaceSize=512m
或修复类加载器泄漏
避免频繁动态代理 / 使用缓存 ClassLoader
StackOverflowError 递归无终止条件、循环调用 1. 看堆栈找重复方法
2. 检查递归出口
添加递归终止条件 / 改为迭代实现 代码审查递归逻辑 / 单元测试边界值
接口响应慢 / CPU 100% 死循环、复杂算法、频繁 GC、线程阻塞 1. top -H -p <pid> 找高 CPU 线程
2. jstack <pid> 找对应线程栈
3. Arthas thread -n 3 查最忙线程
优化算法 / 加缓存 / 异步化 / 调整线程池 性能压测 + Arthas trace 监控热点方法
数据库连接超时 / TooManyConnections 连接未关闭、连接池配置不合理 1. 检查是否用 try-with-resources
2. 查看连接池 active / idle 数
3. show processlist 看 MySQL 连接
使用 HikariCP / Druid 监控 / 设置合理 maxPoolSize 连接必须在 finally 或 try-with-resources 关闭
ConcurrentModificationException 遍历集合时修改结构(非线程安全) 1. 看堆栈定位操作位置
2. 检查是否在 for-each 中 remove/add
使用 Iterator.remove() / CopyOnWriteArrayList / 加锁 使用线程安全容器 / 明确并发修改规则
Spring Bean 注入失败 NoSuchBeanDefinitionException 组件未扫描、@ComponentScan 路径不对、循环依赖 1. 检查 @Service/@Component 是否标注
2. 检查包路径是否被扫描
3. 检查 @Autowired 是否写错
补注解 / 调整扫描路径 / 用 @Lazy 解决循环依赖 使用 IDEA Spring 插件辅助检查 Bean
IllegalArgumentException: Invalid character(URL/参数) 参数含特殊字符未编码、JSON 格式错误 1. 打印原始请求参数
2. 检查前端是否 encodeURIComponent
3. 检查后端是否 URLDecoder.decode
前后端统一编码规范 / 使用框架自动解析 使用 Swagger/Postman 调试接口 / 参数校验注解
定时任务不执行 / 执行多次 未启用 @EnableScheduling、集群未分布式锁、多个实例部署 1. 检查配置类是否有 @EnableScheduling
2. 检查是否多节点部署
3. 日志看是否触发
加 @EnableScheduling / 用 Redis 分布式锁 / Quartz 集群模式 集群环境必须考虑幂等和分布式调度
SSLHandshakeException / HTTPS 调用失败 证书过期、信任库缺失、TLS 版本不匹配 1. openssl s_client -connect host:port
2. 检查 JDK cacerts 是否包含证书
3. 查 TLS 协议版本
导入证书到 truststore / 升级 JDK / 设置协议版本 测试环境导入证书 / 生产使用正规 CA 证书
Maven 编译报错:程序包 xxx 不存在 依赖未下载、仓库地址错误、本地仓库损坏 1. mvn clean compile -U
2. 检查 settings.xml 镜像源
3. 删除 ~/.m2/repository 重下
更换镜像源 / 删除本地仓重下 / 检查网络 使用公司 Nexus 私服 / 配置稳定的阿里云镜像

🧪 四、典型案例详解

▶ 案例1:CPU 100% 排查

# 1. 找出 Java 进程 PID
ps -ef | grep java

# 2. 找出最耗 CPU 的线程
top -H -p <pid>

# 3. 转换线程 ID 为 16 进制
printf "%x\n" <tid>

# 4. 查看线程堆栈
jstack <pid> | grep -A 30 <hex_tid>

# 5. 用 Arthas 快速定位
thread -n 3  # 显示最忙的3个线程

→ 发现是 HashMap.get() 在多线程下形成死循环 → 改为 ConcurrentHashMap


▶ 案例2:内存泄漏排查

# 1. 导出堆 dump
jmap -dump:live,format=b,file=heap.hprof <pid>

# 2. 用 MAT 分析
# 打开 Eclipse MAT → 找 Dominator Tree → 看 Retained Heap 最大的对象

# 3. 用 Arthas 监控对象创建
watch com.example.Service fetchData returnObj -x 3

→ 发现是静态 Map 缓存用户数据且无过期 → 改用 Guava Cache / Caffeine


▶ 案例3:Spring 循环依赖报错

@Service
public class A {
    @Autowired private B b;
}

@Service
public class B {
    @Autowired private A a; // 循环依赖!
}

✅ 解决方案:

@Service
public class B {
    @Lazy  // 延迟初始化 A
    @Autowired private A a;
}

🛡️ 五、预防胜于治疗 —— 日常开发最佳实践

场景 建议
空指针 使用 Objects.requireNonNull(), Optional, @NonNull 注解
资源关闭 一律使用 try-with-resources
异常处理 不吞异常、记录完整堆栈、自定义异常带业务上下文
并发控制 优先使用 java.util.concurrent 包,避免手写 synchronized
配置管理 使用 Spring Boot @ConfigurationProperties + 配置中心
依赖管理 定期 mvn dependency:analyze 清理无用依赖
日志规范 使用 SLF4J + Logback,格式:[类名] [方法] [关键参数] msg
上线前检查 压测 + Arthas trace 热点方法 + SonarQube 扫描

📌 六、排错口诀(背下来!)

一看日志二复现,
三用工具四隔离。
堆栈行号是路标,
最小代码定乾坤。
修复不忘加监控,
单元测试保太平。

更多推荐