稳定性概览
稳定性问题是影响应用可用性和用户体验的核心因素。对终端用户来说,崩溃、卡死、内存异常和资源泄漏往往都会直接表现为页面退出、交互失效、性能恶化或长时间运行后的异常行为。对研发团队来说,稳定性问题不仅影响问题单量和版本质量,也会影响应用评分、留存和关键业务指标。
1. 为什么要先做稳定性分类
稳定性问题一旦发生,通常无法依靠用户自行恢复,往往需要重启应用,严重时还会导致数据丢失、功能中断或页面长时间不可用。因此,排查时不能把所有问题都当成“普通崩溃”处理,而应先明确故障类型,再进入对应的分析路径。
稳定性分类的意义主要体现在以下几个方面:
- 有助于快速选择正确的日志和工具。
- 有助于区分是进程退出、线程阻塞、内存异常还是资源未释放。
- 有助于在框架、适配层和业务调用之间快速缩小范围。
- 有助于形成统一的问题归档和治理口径。
2. 稳定性问题分类体系
HarmonyOS 场景下,稳定性问题通常可归并为四大类。
| 问题类别 | 常见子类型 | 典型特征 | 主要检测方式 |
|---|---|---|---|
| 应用异常退出 | CppCrash、JS Crash | 进程直接退出,伴随崩溃日志、异常栈或错误信号 | 基于操作系统信号和运行时异常检测 |
| 应用冻屏 | 线程阻塞、输入阻塞、生命周期超时 | 界面无响应、点击无反应、应用卡死但进程仍在 | 看门狗机制和超时检测 |
| 内存异常 | OOM、内存泄漏、地址越界 | 内存持续增长、异常分配失败、非法访问 | 内存监控、地址越界检测、堆分析 |
| 资源泄漏 | 句柄泄漏、线程泄漏、长期未释放资源 | 长时间运行后资源数异常增长,最终引发连锁故障 | 周期性巡检、阈值检测和资源统计 |
3. 各类问题的基本定义
3.1 应用异常退出
应用异常退出是指进程在运行过程中意外终止,通常可以细分为两类:
- CppCrash:C/C++ 运行时崩溃,通常由空指针、越界访问、悬空指针、异常终止等问题触发,常见信号包括 SIGSEGV、SIGABRT、SIGBUS、SIGTRAP。
- JS Crash:未处理的 JS 异常导致应用退出,常见错误包括 TypeError、ReferenceError、RangeError 等,典型信息如访问 undefined 属性、调用不存在的方法或初始化阶段抛错未被兜底。
3.2 应用冻屏
应用冻屏是指用户操作后界面长时间没有响应,表现为点击无反应、动画停止、页面卡死或被系统判定为无响应。此类问题不一定导致进程退出,但会直接影响交互可用性。
常见场景包括:
- 主线程长时间执行耗时逻辑。
- 线程间锁顺序不一致导致死锁。
- 输入事件处理超时。
- 生命周期切换期间存在阻塞等待。
3.3 内存异常
内存异常包括两类常见问题:
- OOM:内存申请超过运行环境可分配上限,应用可能直接崩溃,常见特征是分配失败、堆持续膨胀、系统回收压力上升。
- 地址越界:主要用于调试和压测阶段发现非法内存访问问题,常见类型包括越界读写、UAF、野指针和空指针解引用。
3.4 资源泄漏
资源泄漏是指句柄、线程、监听器、定时器、请求对象或内存对象未按预期释放,导致资源长时间占用。资源泄漏在短期内可能只表现为内存缓慢增长,但在长时间运行后通常会继续放大,进一步引发卡顿、冻屏、崩溃或创建失败等连锁问题。
4. RNOH 场景下的高发稳定性问题
在 React Native Harmony 场景中,稳定性问题往往集中在以下几个方向:
- 进程崩溃覆盖 C++ 侧和 JS 侧,既可能来自原生适配层,也可能来自未处理的 JS 异常。
- 应用无响应通常与 JS 线程、UI 线程和 Worker 线程之间的阻塞、同步等待或锁竞争有关。
- 内存异常和资源泄漏容易出现在线程对象、回调对象、实例销毁后的残留引用和长生命周期模块中。
- 框架主要实现位于 C++ 和 ArkTS 适配层时,很多稳定性问题会在生命周期切换、跨线程调度和资源回收阶段暴露。
5. 检测机制概览
不同故障类型的检测口径不同,分析时应采用对应的检测机制:
- CppCrash:基于 Linux 信号机制检测,重点关注崩溃信号、FaultLog、崩溃线程和 native backtrace。
- JS Crash:基于运行时未处理异常检测,重点关注异常类型、错误消息、JS 堆栈和 sourcemap 还原结果。
- AppFreeze:基于看门狗和超时检测,重点区分线程阻塞、输入阻塞和生命周期超时。
- 内存异常:基于内存基线、峰值监控、堆快照和地址越界检测,识别 OOM、泄漏和非法访问。
- 资源泄漏:基于句柄数、线程数、PSS 和对象存活情况的周期性巡检,定位未释放资源。