**诊断口诀**:看堆栈里最近一次自己的代码调用——空指针找中间变量,越界找循环边界,ANR 等主线程,内存耗尽看分配点。
### 4.2 数据采集——为矩阵赋值收集证据
每一种崩溃类型需要针对性采集的关键字段不同。以下排查表列出了必采集项:
| 崩溃类型 | 必采集日志 | 采集合集 | 采样工具 |
|----------|------------|----------|----------|
| 空指针 | 异常栈 + 变量值快照 | 调用链全部类名、方法名、行号 | `Exception.getStackTrace()` / `crash_dump` |
| 数组越界 | 数组长度 + 访问下标 | 循环变量现场值、入参索引 | `-Xcheck:jni`(Java) / AddressSanitizer(C++) |
| 资源泄漏 | 打开文件/句柄列表 | 资源创建时间、线程ID、调用栈 | `lsof` / `strace -e open` / LeakCanary |
| ANR | 主线程堆栈 + CPU/IO耗时 | 所有线程堆栈快照、binder 事务 | `dumpsys activity.anr` / SIGQUIT |
| 内存耗尽 | Heap Dump + 分配栈 | 大对象分配点、GC 日志 | `MAT` / `hprof` / JNI 指针泄漏检测 |
| 非法指令 | CPU 寄存器 + 指令码 | JIT 缓存内容、汇编片段 | `objdump` / `gdb disassemble` |
| 信号异常 | 信号编号 + si_code | 发送信号时的内核上下文 | `sigtrap` / `syscall` 跟踪 |
### 4.3 原因定位——判断矩阵与故障树
以下故障树展示了从日志锚点出发的遍历逻辑:
### 4.4 修复动作——每种类型的定向处理
| 崩溃类型 | 修复动作 | 常见反模式 |
|----------|----------|-----------|
| 空指针 | 加空值保护、使用 `Optional`/提早 fail-fast | 只加 `if(x!=null)` 但不追溯根因 |
| 数组越界 | 修复边界逻辑、使用安全集合(`ArrayList`、`std::vector::at`) | 只 catch 异常不修复循环 |
| 资源泄漏 | 确保 `close()` 在 finally/RAII 中、使用池化 | 只增 `System.gc()` 不释放 |
| ANR | 异步化耗时操作、锁优化、减少 I/O | 用 `new Thread` 但导致线程爆炸 |
| 内存耗尽 | 减少对象引用、检测大对象、使用 WeakReference | 只调大堆限制不治理分配 |
| 非法指令 | 更新 CPU 微码、关闭 JIT 特定优化、重新编译 | 忽略并继续部署 |
| 信号异常 | 检查空指针后再调用、修复 fstat/写越界(SIGSEGV) | 只 catch 不恢复 |
### 4.5 复测——验证修复的“击杀确认”
每个崩溃类型复测时应使用不同的验证手段:
- **空指针**:所有空值路径单元测试覆盖,mock 空对象返回。
- **数组越界**:边界值测试(长度为 0、1、max、max+1)。
- **资源泄漏**:压力测试后检查 `/proc/self/fd` 计数,运行 24h 无增长。
- **ANR**:用 Systrace 或 `atrace` 检测主线程耗时,目标 < 100ms。
- **内存耗尽**:OOM 场景压力测试,使用 `-Xmx` 收紧堆大小验证无崩溃。
- **非法指令**:在不同 CPU 架构(arm64/x86_64)上运行原生测试集。
- **信号异常**:monkey 测试 + 信号捕获(`sigaction`)确认不再出现。
### 4.6 预防——矩阵驱动的常态化防御
> **核心原则**:不要在运行时防御已经能通过编译期/静态分析消除的崩溃。
| 预防纵深 | 对应类型 | 工具/实践 |
|----------|----------|-----------|
| 编译期检查 | 空指针、数组越界 | `@NonNull`/`@Nullable` 注解、SpotBugs、Clang Static Analyzer |
| 静态分析 | 所有类型 | FindBugs、Infer(Meta)、PVS-Studio |
| 单元测试 | 空指针、越界、ANR | 边界值用例 + 异步 timeout 测试 |
| 动态检测 | 内存耗尽、资源泄漏 | LeakCanary(Android)、AddressSanitizer(C++) |
| 代码 Review | 非法指令、信号异常 | 每一处 native 调用检查参数有效性 |
| 发布看板监控 | ANR、OOM | 实时 Crash Dashboard 按类型聚合 |
结合本章的判断矩阵,团队可以在问题发生的最初10秒内选出正确的排查路径,避免在高声量崩溃中走错分叉。