反复打开详情页十次,内存涨了 80MB 还没下来
内存泄漏不一定是“忘了释放”
截图整理工具的详情页,打开一张截图,展示大图、OCR 全文、分类置信度。开发阶段没人在意内存,直到测试同学拿着 DevEco Profiler 跑了一遍:反复打开关闭详情页十次,ArkTS Heap 从 120MB 涨到 200MB,手动触发 GC 之后仍然有 80MB 没有被回收。
增长不一定就是泄漏,但反复走同一条路径、每次增长且 GC 后不回落,就是泄漏的典型特征。排查的起点不是猜哪行代码有问题,是用 Memory Profiler 拍快照对比。

拍两张快照,看谁在持续增长
DevEco Studio 的 Memory Profiler 是排查泄漏的核心工具。步骤不复杂:运行应用,选择进程,等内存稳定后拍摄第一张 Heap Snapshot;然后操作应用,反复打开关闭目标页面十次;手动触发 GC,再拍第二张快照;对比两张快照,重点关注应用自身的类,如果某类对象数量持续增长且不下降,点击查看引用链,找到持有者。
typescript
// 详情页中常见的泄漏写法:未释放的定时器
aboutToAppear(): void {
this.timer = setInterval(() => {
this.refreshConfidence(); // 回调持有页面 this
}, 2000);
}
aboutToDisappear(): void {
// 漏了 clearInterval,定时器持续运行
}
定时器回调闭包持有了页面实例,页面销毁后引用链不断,整个组件树和它持有的图片 PixelMap 都无法回收。修复就是在 aboutToDisappear 里 clearInterval 并把 timer 置为无效值。同样的问题也会出现在未移除的事件监听上,emitter.on 注册的回调持有页面上下文,忘记 emitter.off 同样会造成泄漏。
图片资源的释放是另一个高频泄漏源
详情页展示大图,image.createImageSource 解码出 PixelMap 之后,如果只让局部变量持有,按理说会被 GC 回收。但 PixelMap 的底层内存有一部分在 Native 侧,释放时机不完全由 ArkTS 的 GC 决定。反复解码大图而不显式 release,Native 侧的内存会先于 ArkTS Heap 涨上去。
在页面 aboutToDisappear 里对不再需要的 PixelMap 调用 release(),ImageSource 也要释放。如果图片来自网络或文件,不要每次进页面都重新解码一遍,用 LRU 缓存限制内存中的图片数量,超出上限的主动释放。
全局变量和集合只增不减是隐形炸弹
比定时器更隐蔽的是全局单例里存了页面实例,或者一个缓存 Map 只 set 不 delete。截图分类的缓存如果按 screenshotId 做 key,用户删除了截图之后缓存里的条目不会自动消失,跑一个月下来就是几万条僵尸数据。
排查这类问题的顺序是:先用 Profiler 确认增长的对象类型,再看引用链找到持有者,最后判断这个持有关系是“预期的缓存”还是“意外的泄漏”。缓存要有上限和淘汰策略,定时器和监听器要和页面的生命周期严格对齐。内存的账,不是“跑起来不崩”就算完,是“反复走同一条路径,数字要回得去”。
更多推荐
所有评论(0)