做异常排查这些年,我最大的体会就是:异常不怕多,就怕乱。一个系统跑得越久,越会冒出来各种奇奇怪怪的报错——有时候是Java数组越界,有时候是Windows终端的ConPTY启动失败,有时候是数据库DDL改到一半卡死,甚至工业设备里的校准参数被一次异常断电直接写坏。如果你只是每次遇到一个错就去搜一行错误信息,今天修完明天还会换一个姿势再炸一遍。我习惯把所有异常当成一个“症状库”来做梳理,按来源分、按处理策略分、按排查路径分,最后形成自己的异常速查表。这篇文章就是我多年异常梳理经验的完整复盘,不管你是写Java、调Docker、查Windows日志,还是搞工业检测,都能找到对应的思路和可以直接抄作业的排查步骤。
1. 为什么要做异常梳理:先搞清异常的分类学
很多人一看到“异常”两个字就紧张,觉得系统要挂了。其实异常是系统在跟你说人话,它是诊断问题最明确的线索。真正让人崩溃的不是异常本身,而是没有一套体系去接住它。我建议先把异常做分类,分类清楚以后,处理策略自然就浮出来了。
1.1 异常不是bug,是系统的“症状”
早期我也犯过一个错误:看到异常就当成bug去消灭,只想让它赶紧消失,结果经常按下葫芦浮起瓢。后来我发现,异常更像人体发烧——体温高不是病本身,而是身体在告诉你内部有炎症。数组越界异常表面上是一行代码写错了,实际往往是数据边界校验没做;终端进程启动失败看起来是工具坏了,实际可能是系统库或者环境变量不对;persist分区校准参数损坏,表面是文件坏了,往深了说是电源管理或者写保护机制有问题。
所以我的第一个建议是:遇到异常先别急着改代码,先把异常信息完整记录下来。异常类名、异常消息、堆栈、触发时间、触发前后的操作,这五样东西缺一不可。很多人在群里丢一句话“报错了,怎么办”,别人想帮都帮不了,就是因为信息太碎片。
1.2 按来源划分:开发态、运行态、环境态
我习惯把异常分成三大类,这样排查时能迅速缩小范围。
第一类是开发态异常,也就是代码编译和静态检查阶段能发现的异常。比如Java里常见的“编译期异常”——文件找不到、类没定义、方法签名对不上,这类异常IDE往往直接标红,属于最好处理的。第二类是运行态异常,指代码跑起来之后才暴露的问题,比如数组越界、空指针、异步任务中断、连接池耗尽。这类异常最考验人,因为不一定能稳定复现。第三类是环境态异常,它跟代码无关,是外部环境出了状况,比如Windows驱动加载失败、路由追踪异常、Docker容器内Java进程异常重启、CAN总线通信异常。环境态异常最坑,因为它经常伪装成代码问题。
这三类异常的排查工具完全不同:开发态靠编译器,运行态靠日志和调试器,环境态靠系统事件查看器和网络抓包。如果你把环境态异常拿代码逻辑去分析,大概率会白忙活一场。
1.3 按处理策略划分:可重试、可降级、可恢复、只能人工介入
除了按来源分,我还喜欢按“你能不能处理它”来分,这决定了你的应对动作。
- 可重试:网络抖动、连接超时、临时锁冲突、服务端短暂5xx。这类异常加个重试机制就好,但要注意重试次数和退避策略,别把服务打挂。
- 可降级:缓存服务器不可用、某个非核心依赖报错。这时候可以熔断降级,返回旧数据或者走本地缓存,保证主链路不被拖垮。
- 可恢复:磁盘空间满了、临时文件被占用、数据库连接数打满。这类异常清理完资源就能恢复,关键是做好监控和告警,在变成灾难之前发现它。
- 只能人工介入:硬件损坏、分区数据写坏、校准参数丢失、驱动彻底被杀。这类异常没有代码层面的自动恢复方案,只能人工修复或重新刷写。
我在实际项目中会把异常按这四个策略贴标签,然后写进监控规则。比如“磁盘使用率超过90%”触发告警,但不自动清理,因为自动清理有风险;“依赖接口超时超过1秒”触发重试,但最多重试3次。这套分类表就是团队异常治理的地基。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 编程语言层面的异常:以Java为例的实战梳理
聊完方法论,咱们落到具体场景。开发高频出现的异常,很大一部分集中在Java生态。热搜词里“java中数组越界异常”“编译期异常”“CompletableFuture异常后不再执行其他异步任务”“OncePerRequestFilter异常ControllerAdvice没法捕捉到”都是非常有代表性的痛点,我一个个拆。
2.1 编译期异常与运行期异常:先分清谁在管
Java异常体系分成两大派:checked exception(受检异常)和unchecked exception(非受检异常)。受检异常比如IOException、SQLException,编译器强制你try-catch或者throws,不然编译不过;非受检异常比如NullPointerException、ArrayIndexOutOfBoundsException,编译器不管,运行期才炸。
很多新手觉得受检异常很烦,但我反而觉得它是Java给你的最好福利。它相当于编译器逼你在写代码的时候就思考“文件读取失败怎么办”“数据库连接断了怎么办”,把很多风险前置了。真正难搞的是非受检异常,它不会在你编译时报错,而是等到线上某次特
