Stability Cases
This document anonymizes and整理 typical stability problems, focusing on故障现象, 归类方式, 分析路径 and 修复思路.
1. Application Abnormal Exit Cases
Case 1: SIGSEGV Caused by Async Callback Continuing Execution After Instance Destruction
Symptom
- Application偶现闪退 after page switch or instance destruction.
- FaultLog shows SIGSEGV, crash point located at instance related callback链路.
Analysis
排查发现, async task submission时直接捕获了 object self reference, subsequent task execution时 object already destroyed, eventually in callback访问失效 instance, forming typical UAF or dangling reference access.
Fix思路
- Not直接持有失效风险 high强引用 in async callback.
- Change callback to弱引用 or先校验 instance有效性后再执行.
- Timely解绑 callback and subsequent task in instance destruction phase.
Case 2: SIGSEGV Caused by Improper Release Order Using Already Invalid Resource
Symptom
- Application出现 SIGSEGV in exit, rebuild or cleanup phase.
- Log无法直接指向 business logic,更像底层 object state异常.
Analysis
Further对 call stack and memory behavior分析后,发现问题出在 object release order错误. Upper layer instance destruction后, lower layer scheduler or runtime仍被访问, causing非法访问 triggered when reverse dependency尚未清理完成.
Fix思路
-梳理 dependency chain,先释放 dependency upstream object,再释放被依赖 object. -对销毁顺序增加断言和空值保护. -避免在析构过程中继续触发外部回调.
Case 3: SIGTRAP Caused by Incomplete Interface Return Path
Symptom
- Application闪退 at startup phase.
- Crash signal为 SIGTRAP, call stack落在某个 native interface call后.
Analysis
Problem最终定位为 interface declaration要求返回 object self or status value, but actual implementation遗漏了返回路径. Caller继续基于异常返回结果执行, eventually触发断言或陷阱信号.
Fix思路
-保证接口声明与实现一致. -补齐所有分支的返回路径. -对调用方增加异常状态保护,避免继续沿错误结果执行.
Case 4: SIGABRT Caused by Exception穿透 noexcept Boundary
Symptom
- SIGABRT出现 during runtime.
- Crash栈表面落在通用 runtime或标准库终止逻辑中.
Analysis
分析后发现,真实根因并不在当前栈顶函数本身,而是某个声明为不抛异常的函数内部调用了可能抛出异常的逻辑, exception穿透到 noexcept边界后触发 terminate, eventually表现为 SIGABRT.
Fix思路
-清理不合理的 noexcept声明. -在边界函数内捕获并转换异常. -重新确认真实业务异常来源,而不是只看崩溃栈最上层.
Case 5: JS Crash Caused by JS Object Access Missing Null Check
Symptom
- Application退出, log提示 TypeError.
- Error信息为读取 undefined或 null对象属性失败.
Analysis
Problem通常发生在页面测量,布局回调或异步数据回填场景中. Code假设对象必然存在, but实际在边界情况下对象为空, causing运行时抛出未处理异常.
Fix思路
-在对象属性访问前增加判空保护. -对异步数据和可选字段建立默认值. -将高风险渲染逻辑拆分为可降级分支.
Case 6: JS Crash Triggered by Parameter Type Error in Initialization Phase
Symptom
- Page initialization失败,首屏空白或应用退出.
- Log显示文件操作或模块初始化阶段抛出 JS异常.
Analysis
分析发现, code把目录路径,非法对象或不符合约定的参数传入了只接受文件对象或特定结构的接口, causing初始化阶段直接报错.
Fix思路
-在初始化前校验输入参数类型. -对目录,文件,对象句柄等不同输入做显式分流. -将初始化逻辑中的异常改为可观测,可降级的失败路径.
2. Application Freeze Cases
Case 7: Thread Deadlock Caused by Inconsistent Lock Order
Symptom
- Application不退出,但界面完全无响应. -冻结日志显示多个线程长期等待,主线程无法继续推进.
Analysis
主线程和业务线程对同一组共享资源采用了不同的加锁顺序, eventually形成循环等待. 由于进程仍然存在,因此表面上是"卡死",本质上是典型死锁.
Fix思路
-统一锁顺序. -缩小锁作用域,避免持锁执行回调. -对高风险同步路径改成无锁或异步串行化模型.
Case 8: Page Freeze Caused by Main Thread Executing Time-consuming Creation Logic
Symptom
- Page打开时明显卡顿,严重时被判定为应用无响应.
- trace中可见主线程持续停留在某个页面创建或节点构建过程.
Analysis
问题出在页面首次渲染阶段存在大量同步构建和高频回调, causing主线程长时间被占用, input事件和生命周期推进都无法及时完成.
Fix思路
-将重计算和重构建逻辑移出主线程关键路径. -避免在页面创建阶段触发异常高频的组件生命周期回调. -对大型组件做分段创建或惰性初始化.
3. Memory Abnormality Cases
Case 9: OOM Triggered by Memory Continuous Growth
Symptom
- Application运行一段时间后内存持续升高, eventually崩溃或被系统回收. -堆分析显示对象总量和占用持续增长,没有回落到基线.
Analysis
问题不一定来自单次大对象分配,更常见的是页面切换,组件重建或长期运行过程中对象不断累积, but销毁路径没有真正释放, eventually放大为 OOM.
Fix思路
-建立内存基线并做阶段性对比. -抓取前后堆快照,识别持续存活对象. -优先检查页面退出后仍存活的上下文,缓存和实例对象.
4. Resource Leak Cases
Case 10: Object Long-term Alive Caused by ArkTS-side Listener Not Unregistered
Symptom
- Page退出后内存没有回落. -快照中仍能看到与页面相关的上下文对象和回调链路存活.
Analysis
根因是模块注册了环境或生命周期回调,但在销毁阶段没有执行 off或反注册, causing回调闭包持续持有上下文对象, forming稳定的泄漏链.
Fix思路
-所有 on注册都必须有成对的 off. -在模块销毁阶段集中清理回调和监听器. -对长期对象持有链做快照回归检查.
Case 11: Long-term Memory Leak Caused by Native Instance Not Released
Symptom
- 多次进入退出同一功能后, Native内存持续增长.
- Allocation视图中可见多个同类实例残留.
Analysis
调用栈分析表明,实例创建流程被多次执行, but调度器,组件注册表或运行时关联对象没有在退出时同步释放, eventually导致 Native侧对象累计.
Fix思路
-建立实例创建与销毁的对账关系. -对调度器,注册表,上下文和任务队列逐项检查释放闭环. -在同步等待路径中避免因为线程阻塞而跳过清理流程.
5. General Experience Extracted from Cases
综合这些案例,可以提炼出几个高频判断规律:
- 销毁后崩溃,优先检查回调,弱引用和对象释放顺序.
- 无响应不等于没日志,重点看主线程,关键线程和 trace.
- 首屏或初始化异常,优先检查参数契约,返回路径和异常边界.
- 长时间运行后问题放大,优先检查监听器,实例,线程和缓存是否真正释放.
- 看到表面栈顶并不等于找到根因,很多稳定性问题需要回到生命周期和线程模型才能解释清楚.