HarmonyOS 7 新特性(二十三)|hiprofiler + hidumper:跨堆内存分析

本文基于 HarmonyOS 7 配套的 hiprofiler、hidumper 与资源泄漏检测资料。不同工具的参数、输出格式和设备权限会随版本变化,请以当前 DevEco Studio 与系统文档为准。
内存问题很少只存在于一个堆。ArkTS 页面可能持有 Native 资源,Web 组件拥有独立运行时,图片和渲染又会消耗 GPU、DMA 或共享内存。只盯着 JavaScript heap,常常会得出“没有泄漏”的错误结论。
HarmonyOS 7 的新资料进一步完善 hiprofiler 录制分配与回收、hidumper 导出 ArkTS、Native、GPU、JSVM、ArkWeb、KMP 等内存信息的分析路径。本文给出一套从现象分类到根因闭环的实战方法。
一、先区分泄漏、峰值和缓存
泄漏是资源在业务结束后仍无法释放;峰值可能来自瞬时大图、视频帧或编译;缓存则会保留但应在压力或上限到达时回落。三者处理方法不同。
设计固定复现脚本:进入页面、执行核心操作、退出页面、等待回收,重复 10 到 30 轮。每轮记录 ArkTS heap、RSS、GPU、句柄与线程。若基线逐轮上升且不回落,才进入泄漏分析。
二、建立统一采样模型
interface MemorySample {
timestamp: number
phase: 'before' | 'active' | 'after-gc' | 'background'
rssKb: number
arktsHeapKb: number
nativeHeapKb?: number
gpuKb?: number
fdCount: number
threadCount: number
}
interface MemoryRun {
device: string
buildId: string
scenario: string
samples: MemorySample[]
}
所有工具输出最终汇总到同一时间线,避免分别看截图后凭感觉关联。
三、hiprofiler 适合看分配过程
hiprofiler 记录一段时间内的分配、调用栈和释放行为,适合回答“哪类对象在什么时候开始增长”“退出页面后哪些分配没有回收”。录制区间应覆盖操作前基线、问题动作和退出后的稳定期。
采样太短可能错过慢泄漏,采样太长又会产生巨大文件。先用粗粒度指标定位窗口,再在 30 到 90 秒的关键区间进行高精度录制。
Round 0: open page -> idle -> baseline
Round 1: import image -> edit -> close -> wait
Round 2: import image -> edit -> close -> wait
Marker: user_action=close_editor
Stop: after memory remains stable for 10s
在业务动作处写稳定 Marker,可以显著降低工具时间轴与用户步骤对齐的成本。

四、hidumper 用于横向快照
hidumper 更适合在关键时刻导出多个内存域的状态。建议至少抓三份:首次进入后的基线、重复操作后的高点、退出并等待后的回落点。文件名写入轮次与阶段,禁止手工覆盖。
function snapshotName(runId: string, round: number, phase: string): string {
const safePhase = phase.replace(/[^a-z0-9-]/gi, '_')
return `${runId}-r${round}-${safePhase}-${Date.now()}`
}
如果 ArkTS heap 回落但 RSS 与 GPU 不回落,应转向 PixelMap、Native 缓冲区、渲染资源和文件映射,而不是继续检查页面对象。
五、用所有权表缩小根因
每个重资源都应该有创建者、持有者和释放时机。常见对象包括订阅、定时器、PixelMap、ImageSource、文件描述符、Native handle、Web 页面、Surface 与线程。
interface OwnedResource {
id: string
type: 'timer' | 'listener' | 'pixelMap' | 'fd' | 'native' | 'web'
owner: string
createdAt: number
releasedAt?: number
}
class ResourceLedger {
private records = new Map<string, OwnedResource>()
open(resource: OwnedResource) { this.records.set(resource.id, resource) }
close(id: string) {
const item = this.records.get(id)
if (item) item.releasedAt = Date.now()
}
}
这不是替代系统工具,而是让工具发现的调用栈可以回到明确业务所有者。
六、ArkTS 泄漏看引用链
典型问题包括全局数组持有页面、事件总线未取消、闭包捕获大对象和长生命周期单例保存 UI 状态。快照中先按 Retained Size 排序,再沿引用链找到 GC Root。
aboutToAppear() {
this.unsubscribe = eventBus.on('assetChanged', this.onAssetChanged)
}
aboutToDisappear() {
this.unsubscribe?.()
this.unsubscribe = undefined
this.previewPixelMap?.release()
this.previewPixelMap = undefined
}
只把字段赋值为 undefined 不一定足够,Native 资源还需要调用明确释放接口。
七、句柄和线程泄漏同样危险
文件导入、数据库、Socket 和事件对象都消耗句柄;工作线程、解码线程与 Web 线程可能在页面退出后继续存在。系统资源泄漏检测会周期采样并在超过阈值时生成日志,但项目不应等到系统管控才修复。
为每个入口写压力测试,检查打开与关闭次数是否成对。网络失败、取消选择和异常返回路径尤其容易漏掉 close。
async function withFile<T>(uri: string, action: (fd: number) => Promise<T>) {
const file = await fileService.open(uri)
try {
return await action(file.fd)
} finally {
await fileService.close(file)
}
}
八、GPU 与大图需要独立观察
图片页面可能出现 ArkTS 对象数量稳定,但 GPU 内存不断增长。检查纹理缓存、离屏渲染、动画图帧缓存和未释放的 PixelMap。为缓存设置条目数与字节双上限,并在后台、低内存与账号切换时清理。
如果问题只在特定分辨率或窗口尺寸出现,要把输入尺寸、色彩空间、HDR 和缩放策略加入复现记录。
九、修复必须通过多轮回落证明
修复后用同一构建类型、同一设备、同一数据和同一脚本重复测试。比较的不是最后一张截图,而是每轮结束后的稳定基线斜率。
function baselineSlope(samples: MemorySample[]): number {
const after = samples.filter(x => x.phase === 'after-gc')
if (after.length < 2) return 0
return (after.at(-1)!.rssKb - after[0].rssKb) / (after.length - 1)
}
合理缓存可能使第一轮升高,之后趋于平台;真正泄漏通常保持正斜率。
十、上线门禁
- 固定复现脚本与采样阶段;
- hiprofiler 时间线包含业务 Marker;
- hidumper 同时覆盖 ArkTS、Native、GPU 与句柄;
- 每类重资源都有所有权和释放时机;
- 异常、取消、后台路径都验证释放;
- 修复后至少重复 20 轮观察基线回落;
- 诊断文件不包含不必要的用户数据。

结语
复杂应用的内存是一组相互关联的资源域。hiprofiler 负责告诉我们“增长发生在何时和何处”,hidumper 帮助比较关键快照,业务所有权表则把工具证据落到具体修复。只有跨堆观察并用重复回落证明,才能真正关闭泄漏问题。
官方参考
- Resource Leak 检测:https://developer.huawei.com/consumer/cn/doc/doccenter-capabilities/resource-leak-guidelines
- 2026 年 7 月开发者月刊:https://developer.huawei.com/consumer/cn/monthly/202607
更多推荐


所有评论(0)