资源泄漏采集开发实践

发布时间:2026/8/1 11:01:51

资源泄漏采集开发实践 本原创文章帖发布在华为开发者联盟社区欢迎开发者前往访问评论交流更多与该内容相关讨论请点击原帖查看资源泄漏采集开发实践-华为开发者话题 | 华为开发者联盟概述资源调优和资源泄漏是应用开发中常见且难以快速解决的两类棘手问题。在鸿蒙应用开发过程中这些问题通常难以利用常规调试手段如hilog流水日志、/proc/[pid]/smaps、/proc/meminfo、/proc/memview、hidumper --mem [pid]直接定位和处理。资源类型描述文件描述符通过open/fopen、epoll、eventfd、socket、pipe、dup等创建的fd。线程通过线程函数创建的线程如pthread_create。Native 内存包含堆内存和映射区内存通过malloc/calloc/realloc/new分配的堆内存NativeHeap; 通过mmap映射的映射区内存包含so等文件映射以及匿名映射GPU内存通过图形标准API如opengles、vlukan、opencl创建的纹理texture、缓冲区buffer等内存。全局句柄 Global Handle在Native侧通过napi_create_reference创建的ArkTS对象全局引用napi_ref。为快速解决上述两类痛点问题鸿蒙系统通过Performance Analysis Kit开放的HiDebug资源采集接口OH_HiDebug_StartProfiler/OH_HiDebug_StopProfiler提供线上资源分配栈采集功能赋能开发者高效实现问题的自诊断与自闭环。该资源采集接口性能功耗开销较大盲目或高频调用易触发应用卡顿、发热。集成时请务必谨慎评估开销建立严格的条件触发策略和配置合理的采集参数兼顾线上功能正常运行和性能开销的平衡。本文将介绍以下内容• HiDebug资源采集简介• 资源泄漏检测流程• 场景案例实现原理HiDebug资源采集简介为帮助开发人员快速定位进程内的资源泄漏问题HarmonyOS提供了HiDebug资源采集能力HiDebug API参考 开发者可调用OH_HiDebug_StartProfiler / OH_HiDebug_StopProfiler接口针对指定资源类型启动采集在采集窗口内记录资源分配的调用栈与统计信息并将结果输出为.htrace文件。开发者可将生成的.htrace文件导入DevEco Studio Profiler进行关联分析按调用栈分组统计频率最高的调用栈即为泄漏嫌疑点从而加速定位泄漏代码位置降低定界定位成本。OH_HiDebug_StartProfiler支持以下资源类型类型枚举值说明OH_RES_TYPE_FD0文件描述符OH_RES_TYPE_THREAD1线程OH_RES_TYPE_NATIVE2Native内存包含堆内存和映射区内存OH_RES_TYPE_GPU3GPU内存OH_RES_TYPE_GLOBAL_HANDLE4全局句柄Native持有的ArkTS对象引用资源泄漏通常是因为资源被分配后未在生命周期结束时正确释放常见原因包括• FD泄漏open/fopen、epoll、eventfd、socket、pipe、dup等创建的fd打开后未调用close。• 线程泄漏pthread / std::thread创建后未正确join / detach或线程函数未正常退出。• Native内存泄漏通过malloc/calloc/realloc/new分配的堆内存NativeHeap未free / delete; 通过mmap映射的映射区内存包含so等文件映射以及匿名映射未munmap。• GPU内存泄漏纹理、缓冲区等GPU资源创建后未释放。• 全局句柄泄漏Native层对ArkTS对象创建了持久化强引用如napi_create_reference后未napi_delete_reference。生成文件类型介绍文件类型介绍.htrace记录采集时间窗口内资源分配的调用栈与统计信息包含分配频次、栈深度、归属线程等。可导入DevEco Studio Profiler按调用栈分组统计定位泄漏嫌疑点。错误码OH_HiDebug_StartProfiler与OH_HiDebug_StopProfiler返回HiDebug_ErrorCode取值如下HIDEBUG_RES_PROF_SUCCESS与HIDEBUG_RES_PROF_FAILURE为两接口共有HIDEBUG_RES_PROF_NOT_STARTED仅StopProfiler返回其余仅StartProfiler返回错误码含义处理建议HIDEBUG_RES_PROF_SUCCESS操作成功启动/停止HIDEBUG_RES_PROF_INVALID_ARG资源采集参数无效检查callback与config是否合法HIDEBUG_RES_PROF_INVALID_MAX_DURATION资源采集最大持续时间参数无效检查maxDuration取值HIDEBUG_RES_PROF_INVALID_FILTER_SIZE资源采集过滤大小参数无效检查filterSize取值HIDEBUG_RES_PROF_INVALID_MAX_STACK_DEPTH资源采集最大回栈深度参数无效。检查maxStackDepth取值。HIDEBUG_RES_PROF_INVALID_STATISTICS_INTERVAL资源采集统计间隔参数无效。检查statisticsInterval取值。HIDEBUG_RES_PROF_INVALID_SAMPLE_INTERVAL资源采集采样大小参数无效。检查sampleInterval取值。HIDEBUG_RES_PROF_INVALID_RESOURCE_TYPE资源采集类型参数无效。检查type取值0/1/2/3/4。HIDEBUG_RES_PROF_PERMISSION_DENIED权限不足仅支持采集调用接口进程本身。仅采集本进程。HIDEBUG_RES_PROF_ALREADY_STARTED资源采集重复启动。先Stop再Start确保配对调用。HIDEBUG_RES_PROF_PROCESS_OVERLIMIT资源采集进程数超出限制单应用内2个进程。减少同时采集的进程数。HIDEBUG_RES_PROF_CONFLICT与开发者工具或系统采集任务冲突。先退出DevEco Studio Profiler / hiprofiler_cmd。HIDEBUG_RES_PROF_DAILY_QUOTA_EXCEEDED每日配额超出限制整机4次 / 单应用2次。减少采集次数或延长maxDuration。HIDEBUG_RES_PROF_CPU_OVERLOADED系统CPU占用率超过70%。等待CPU负载降低后重试。HIDEBUG_RES_PROF_MEM_PRESSURE_CRITICAL内存可用空间低于2GB。释放内存后重试。HIDEBUG_RES_PROF_STORAGE_PRESSURE_CRITICAL存储可用空间低于15GB与整机总量3%中的较大值。清理存储空间后重试。HIDEBUG_RES_PROF_NOT_STARTED资源采集未启动停止失败先调用StartHIDEBUG_RES_PROF_FAILURE启动/停止资源采集失败排查系统状态后重试资源泄漏检测流程应用启动后通过NAPI拉起ResourceProfilerManager单例。• 触发采集的三条路径择一执行且任一时刻仅允许一路采集(1) C 统一轮询后台线程按各自周期检测FD / Thread / Native / GPU四类资源Native每200s、其余每60sNative与GPU需连续2次超阈值、FD与Thread单次超阈值即自动触发对应类型的采集避免偶发波动误触发。资源类型触发采集条件 推荐值检测间隔 推荐值参考建议文件描述符FD超过5000个60s1、开发者可通过读取/proc/self/fd_num节点获取被采集应用的FD总数。2、1个检测周期内每60s若发现应用的FD总数超过阈值5000个则认为可能存在FD泄漏此时可调用资源分配栈采集接口采栈。线程超过700个60s1、开发者可通过读取/proc/self/status节点的Threads字段获取被采集应用的线程总数。2、1个检测周期内每60s发现线程总数超过700个则认为可能存在线程泄漏此时可调用资源分配栈采集接口采栈。Native内存包含堆内存和映射区内存超过3GB200s1、开发者可通过读取/proc/self/status节点将VmRssVmSwap字段之和作为被采集应用占用的Native内存总量。注应用Native内存超4GB且整机内存压力过大时可能触发系统管控建议超3GB或更小时就启动采集。2、连续2个检测周期每200s一个周期Native内存均超过阈值3GB则认为可能存在Native内存泄漏此时可调用资源分配栈采集接口采栈。GPU内存超过2.3GB60s1、开发者可通过调用OH_HiDebug_GetGraphicsMemory获取被采集应用的GPU内存总量。2、连续2个检测周期每60s一个周期GPU内存均超过阈值则认为可能存在GPU内存泄漏此时可调用资源分配栈采集接口采栈。全局句柄Global HandleVM堆内存使用率超过70%60s1、开发者可通过hidebug.getAppVMObjectUsedSize() / hidebug.getAppVMMemoryInfo().totalHeap 0.7作为触发采集条件。2、1个检测周期内每60sVM堆内存使用率超过70%则认为可能存在Global Handle泄漏此时可调用资源分配栈采集接口采栈。说明 以上Native内存和GPU内存触发采集条件推荐值均基于12GB RAM设备其他RAM大小设备采集阈值请开发者自行根据实际情况调整。(2) ArkTS定时检测ArkTS侧通过setInterval周期检测VM堆内存使用率超过阈值即触发Global Handle采集。(3) 手动按需采集在可疑业务函数执行前后调用OH_HiDebug_StartProfiler/ OH_HiDebug_StopProfiler采集该时间窗口内的资源分配数据。• OH_HiDebug_StartProfiler接口调用后采集器会记录资源分配数据。• 采集结束调用OH_HiDebug_StopProfiler或maxDuration到期自动停止后系统生成.htrace文件并通过回调返回其沙箱路径。• 将.htrace文件导入DevEco Studio Profiler按调用栈分组分析定位泄漏点。场景案例场景描述开发人员观测到应用进程的Native内存持续增长、或/proc/self/fd条目数异常增多、或/proc/self/status中Threads值持续上升需要定位是哪段代码在泄漏资源、并分析其调用栈。开发步骤1. 添加依赖Native侧在CMakeLists.txt中链接HiDebug NDKtarget_link_libraries(entry PUBLIC libace_napi.z.so ohhidebug.so)ArkTS侧引入PerformanceAnalysisKit与Native模块import { hilog, hidebug } from kit.PerformanceAnalysisKit; import resourceProfiling from libentry.so;2. 接入资源采集管理器ResourceProfilerManager是对OH_HiDebug_StartProfiler / OH_HiDebug_StopProfiler的单例封装统一管理采集状态与回调对外暴露采集接口与统一轮询接口。其公开接口签名如下class ResourceProfilerManager { public: static ResourceProfilerManager GetInstance() { static ResourceProfilerManager instance; return instance; } // Basic profiling interface with custom config HiDebug_ErrorCode StartProfiling( OH_HiDebug_ResourceType type, OH_HiDebug_ResProfilerConfig config, std::functionvoid(OH_HiDebug_ProfilingResult*) userCallback); // Basic profiling interface with default config HiDebug_ErrorCode StartProfiling( OH_HiDebug_ResourceType type, std::functionvoid(OH_HiDebug_ProfilingResult*) userCallback); HiDebug_ErrorCode StopProfiling(); bool IsProfiling() const; // Unified polling detection interface // Polls multiple resource types; triggers profiling for the matching type // when any one exceeds its threshold. // Skips triggering if a profiling task is already running. void StartUnifiedPolling( std::functionvoid(const char* resultPath, OH_HiDebug_ResourceType type) onResult); void StopUnifiedPolling(); bool IsPolling() const; private: static void InternalProfilingCallback(OH_HiDebug_ProfilingResult* result); void PollingLoop(std::functionvoid(const char* resultPath, OH_HiDebug_ResourceType type) onResult); std::atomicbool m_isProfiling{false}; std::functionvoid(OH_HiDebug_ProfilingResult*) m_userCallback; std::atomicbool m_isPolling{false}; std::thread m_pollingThread; };OH_HiDebug_ResProfilerConfig各参数含义如下默认配置已在开发实践示例代码中内置开发者亦可按场景自定义参数类型默认值含义maxDurationuint32_t30采集时长上限秒到期自动停止filterSizeuint32_t256过滤器大小maxStackDepthuint32_t30最大栈深度sampleIntervaluint32_t384采样间隔字节statisticsIntervaluint32_t10统计间隔秒完整实现含轮询主循环、阈值检测、回调处理参考示例代码。3. 启动统一轮询检测调用startUnifiedPolling即可启动C 后台线程对FD / Thread / Native / GPU四类资源按各自周期轮询Native每200s、其余每60sNative与GPU需连续2次超阈值、FD与Thread单次超阈值即自动触发采集并通过回调返回结果文件路径// Start polling with a callback resourceProfiling.startUnifiedPolling((path: string, type: number) { let typeName Unknown; switch(type) { case 0: typeName FD; break; case 1: typeName Thread; break; case 2: typeName Native Mem; break; case 3: typeName GPU Mem; break; case 4: typeName Global Handle; break; default: typeName Type ${type}; break; } const msg [${typeName}] Profiling Finished!\\nPath: ${path}; hilog.info(0x3300, ResourceProfiling, msg); });统一轮询内置阈值与触发策略FD数量5000个、线程数700个、Native内存3GB、GPU内存2.3GNative每200s轮询且需连续2次超阈值、GPU每60s轮询且需连续2次超阈值、FD与Thread每60s轮询且单次超阈值即触发采集。4. ArkTS侧VM Heap检测Global Handle泄漏由ArkTS侧独立检测。通过hidebug.getAppVMMemoryInfo()获取VM堆信息当已使用占比超过70%时触发Global Handle采集// VM heap memory detection threshold (70%) const VM_HEAP_THRESHOLD 0.7; let vmTimer: number -1; function detectVMHeapLeak(onResult: (path: string) void): void { try { const vmInfo hidebug.getAppVMMemoryInfo(); const heapUsed hidebug.getAppVMObjectUsedSize(); if (vmInfo.totalHeap 0 (heapUsed / vmInfo.totalHeap) VM_HEAP_THRESHOLD) { // Call NAPI startProfiling with type 4 (OH_RES_TYPE_GLOBAL_HANDLE) const ret: number resourceProfiling.startProfiling(4, 180, 256, 30, 384, 10); if (ret 0) { // HIDEBUG_RES_PROF_SUCCESS // Set timeout to stop profiling after 180s setTimeout(() { resourceProfiling.stopProfiling(); }, 180000); } } } catch (e) { hilog.error(0x3300, ResourceProfiling, VM heap detection failed: %{public}s, JSON.stringify(e)); } } // Start periodic VM heap memory detection (60s) vmTimer setInterval(() { detectVMHeapLeak((path: string) { const msg [Global Handle] VM Heap Exceeded!\\nPath: ${path}; hilog.info(0x3300, ResourceProfiling, msg); }); }, 60000);5. 手动按需采集当能定位到可疑的业务函数时可在其执行前后手动启停采集精确覆盖该业务窗口内的资源分配// Manually start profiling for FD (type0) resourceProfiling.startProfiling(0, 30, 256, 30, 384, 10); // ...execute the suspicious business logic (profiler records allocations after Start) resourceProfiling.stopProfiling();6. 文件导出采集完成后回调中的result-filePath即为结果文件沙箱路径格式如下/data/storage/el2/base/files/类型-被采集应用进程名-进程号-时间戳.htrace示例/data/storage/el2/base/files/native-com.example.resourceleakprofilingdemo-33367-20260708_113011.htrace文件导出参考应用沙箱路径和真实物理路径的对应关系。7. 关闭检测抓取到所需维测数据后停止轮询与定时检测// Stop polling and VM detection when the page is destroyed if (vmTimer ! -1) { clearInterval(vmTimer); } resourceProfiling.stopUnifiedPolling();8. 分析生成的文件将.htrace文件导入DevEco Studio Profiler后按调用栈分组统计出现频次最高的调用栈即为泄漏嫌疑点据此跳转到对应源码位置完成修复可参考内存分析介绍。分析要点• 以调用栈为维度分组关注高频出现的栈。• 同一栈反复出现说明对应代码路径存在资源分配未释放。• 结合业务逻辑确认该路径是否缺少对应的释放调用。采集规格约束采集配额约束采集配额分整机与应用两级管控• 整机级所有应用共享每日最多采集4次同一时刻最高支持4个不同应用并行采集。• 应用级单应用独享每日最多采集2次同一时刻最高支持应用内2个进程并行采集。超出配额时返回HIDEBUG_RES_PROF_DAILY_QUOTA_EXCEEDED或HIDEBUG_RES_PROF_PROCESS_OVERLIMIT。说明多个应用进程并行采集会对整机性能产生较大影响请开发者根据实际情况谨慎评估后使用。采集负载熔断机制当系统触发以下任一负载保护条件时采集请求将被拒绝并返回相应错误码• CPU限制整机系统CPU占用率超过70%返回HIDEBUG_RES_PROF_CPU_OVERLOADED。• RAM限制整机剩余可用内存RAM低于2GB返回HIDEBUG_RES_PROF_MEM_PRESSURE_CRITICAL。• ROM限制整机剩余可用存储空间ROM低于15GB与整机总量3%中的较大值返回HIDEBUG_RES_PROF_STORAGE_PRESSURE_CRITICAL。采集并发冲突约束本API与开发者工具 DevEco Studio Profiler/hiprofiler_cmd或系统采集任务存在排他性发生冲突时采集请求将被拒绝并返回HIDEBUG_RES_PROF_CONFLICT使用前先退出相关工具或任务。采集文件老化管控每种资源类型的采集文件均受以下滚动老化约束• 按文件个数只保留最新5份文件超出后滚动删除最早文件。• 按单文件大小单文件上限1GB。• 按目录空间沙箱中采集文件存储空间超过4GB时删除最早的采集文件。• 按存储时效每个采集文件最大存储时长24小时到期后清理。采集性能功耗与触发策略受上述采集规格与约束限制本API不保证采集请求一定成功。调用本API会对当前应用进程及整机性能功耗产生不可忽略的影响包括但不限于CPU、内存占用升高丢帧卡顿、发热。集成时必须谨慎评估性能功耗开销建立严格的条件触发策略如仅在特定灰度范围内、应用资源超基线阈值时触发并配置合理的采集参数以兼顾性能功耗平衡后再决策是否开启。严禁在生产环境下盲目或高频次触发。采集接口调用约束• OH_HiDebug_StartProfiler/OH_HiDebug_StopProfiler配对未配对调用会导致HIDEBUG_RES_PROF_ALREADY_STARTED确保一次采集完整覆盖一个业务场景后再启动下一次。• 采集时间窗口语义采集器仅记录OH_HiDebug_StartProfiler之后的资源分配数据OH_HiDebug_StartProfiler前已分配的资源不会被采集因此接口调用采集建议包裹完整业务时间窗口。示例代码• 资源泄漏采集示例-----------------------------------------------------------------------------------------------------官网开发者学堂视频华为开发者学堂社区DFX专题文章华为开发者问答 | 华为开发者联盟【扫码加入 HarmonyOS DFX 技术交流群】

相关新闻