内存泄漏系列专题分析之五:使用malloc_debug定位C/C++ native heap内存泄露
【关注我后续持续新增专题博文谢谢】上一篇我们讲了内存泄漏系列专题分析之四Android malloc_debug工具在Camera领域使用中预览卡死的瓶颈限制问题和二次改造这一篇我们开始讲内存泄漏系列专题分析之五使用malloc_debug定位C/C native heap内存泄露目录一、Malloc Debug配置说明1.1常见配置1.2malloc debug log日志1.3native_heapdump_viewer.py 使用二、使用Malloc Debug2.1打开Malloc Debug2.2分析原始dump文件2.3native_heapdump_viewer.py 格式化2.4解析调用栈2.5分析调用栈对应代码2.6总结本文主要介绍使用Google原生Android自带的malloc debug帮助定位C/C native heap内存泄露的方法。一、Malloc Debug配置说明1.1常见配置front_guard[SIZE_BYTES] 每次调用 malloc ,都在分配的区域之前填充 SIZE_BYTES ,填充内容为 0xaarear_guard[SIZE_BYTES] 每次调用 malloc ,都在指向内存的最后填充 SIZE_BYTES ,填充内容为 0xbbguard[SIZE_BYTES] 这个选项包含了 front_guard 和 rear_guard 。在指向内存的前后连 续 SIZE_BYTES 分别填充 0xaa 和 0xbbbacktrace[MAX_FRAMES] 这 个 optipn s 会 将 内存分配的速度减慢一个数量级 ,MAX_FRAMES 最大 值 256 默认值 16 。 每次在调用 malloc 时 , malloc debug 都会记录 malloc 的调用栈 (trace).栈的最大深度为 MAX_FRAMES , MAX_FRAMES 越大 , 对 malloc 的性能影响越大 , 也就越慢。当进程收到信号 SIGRTMAX - 17 Android 通常该信号值为 47 时, 会触发 malloc debug 的 dump heap trace 功能。 默认 dump 路径在 /data/local/tmp/ backtrace_heap.PID.txt 。给进程发信号通过 kill -s 47 PID , 进程收 到信号后并不会马上 dump backtrace , 而是会等到下次调用 malloc 或者 free 时 才会触发。所以如果发送信号后没有产生 trace 文件,请继续针对调试的进程做 测试。执行 kill -s 47 PID 之后,系统开始进行等待用户执行 malloc 操作。 举例 setprop libc.debug.malloc.options backtrace5 得到的 dump 文件为 $pid.txt 结尾backtrace_enable_on_signal[MAX_FRAMES] :使能这个选项 , 通过给进程发送信号 45 , 可以动态开启和关闭 backtrace 功能 。backtrace_dump_on_exit进程退出后自动 dump trace 文件,得到的 dump 文件为 $pid.exit.txt 结尾backtrace_dump_prefixtrace dump 的路径 , 如果需要放置其他目录 , 如 /sdcard/heap, 则 dump 的文件路 径为 /sdcard/heap.$PID.txtleak_track程序结束后,如果有未 free 的指针, logcat 中会打印出来12-19 14:54:32.583 7971 7971 E malloc_debug: *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 12-19 14:55:02.585 7971 7971 E malloc_debug: androidtest leaked block of size 3072 at 0x736e78f030 (leak 1 of 7) // 内存泄露的 log ,直到程序退出未释放的内存 12-19 14:55:02.585 7971 7971 E malloc_debug: Backtrace at time of allocation: 12-19 14:55:02.585 7971 7971 E malloc_debug: #00 pc 00000000000152b0 /apex/com.android.runtime/lib64/libc_malloc_debug.so (debug_calloc432) 12-19 14:55:02.585 7971 7971 E malloc_debug: #01 pc 000000000000114c /system/bin/androidtest // 通过 addr2line -e symbols/system/bin/androidtest 000000000000114c 可以得到具体是哪一个指针未释放内存。 12-19 14:55:02.585 7971 7971 E malloc_debug: #02 pc 000000000007d86c /apex/com.android.runtime/lib64/bionic/libc.so (__libc_init108) 12-19 14:55:02.585 7971 7971 E malloc_debug: #03 pc 000000000000104c /system/bin/androidtest 12-19 14:55:02.585 7971 7971 E malloc_debug: #04 pc 00000000000533f4 /apex/com.android.runtime/bin/linker64 12-19 14:55:02.585 7971 7971 E malloc_debug: androidtest leaked block of size 88 at 0x736e631030 (leak 2 of 7)record_allocs[TOTAL_ENTRIES]该选项很占内存 , 建议不开启。对进程中使用 malloc, calloc, realloc 的地方进行记录,打印的格式如下Threadid: action pointer size 471: malloc 0x72e00330c0 6 471: realloc 0x72e0005220 0x72e00330c0 12 471: free 0x72e012fcc0 471: free 0x72e0005220 471: malloc 0x72e01ade40 56 471: malloc 0x72e00330c0 6 注意,最大记录 8,000,000 条1.2malloc debug log日志verbose 打开 malloc debug 更多 log ,类似08-16 15:54:16.060 26947 26947 I libc : /system/bin/app_process64: malloc debug enabled09-10 01:03:50.070 557 557 I malloc_debug: /system/bin/audioserver: Run: kill -47 557 to dump the backtrace.1.3native_heapdump_viewer.py 使用该工具可以将 dump trace 文件通过符号表得到当前 未释放内存的指针在代 码中的行号 。准确性依赖 trace 保存栈的最大深度。所以最好有两份该文件,分 别是不同占用内存时 dump 得到的。对比查看哪个指针嫌疑最大,缩小范围继 续排查。其中 symbols 路径要正确二、使用Malloc Debug本文主要介绍使用Google原生Android自带的malloc debug帮助定位C/C native heap内存泄露的方法。2.1打开Malloc Debug针对android.hardware.graphics.composer2.1-service打开malloc debug#!/bin/bash# 需要重启上层让 property 生效stop# 指定 debug 参数。可以默认这样写更多选项参考# bionic/libc/malloc_debug/README.mdsetproplibc.debug.malloc.optionsbacktrace16 guard8 fill_on_free16# 指定要开malloc debug的进程setproplibc.debug.malloc.programandroid.hardware.graphics.composer2.1-service# 进程重启用libc_deubg.so替换原来的libc.SO库mallocDebug代码生效kill -9 $(pidof android.hardware.graphics.composer2.1-service)start# 需要关闭SELinux和目录读写权限setenforce 0chmod 777 /data/local/tmp# 触发Malloc Debug dump默认会生成如下dump文件# /data/local/tmp/backtrace\_heap.**PID**.txtkill -47 $(pidof android.hardware.graphics.composer2.1-service)2.2分析原始dump文件原始dump展现形式:1. 第一部分主要包含分配内存的大小分配的次数分配内存的函数调用栈地址。相同的调用栈用一行表示。z 0 sz 242952 num 1 bt 776cd9667c 776cd964a8 776d7977bc 776a16e384 776a16e6d0 776a16e4d8 776a1701bc 776a16fff0 776a188b48 776a188a0c 776a175394 776a179038 7769881260 7769878458 7769862208 776985ce4c z 0 sz 163840 num 1 bt 776cd9667c 776cd964a8 776a1937b4 776a1913d4 776a198cf8 776a199128 776a16fa80 776a1703cc 776a170328 776a188a30 776a175394 776a179038 7769881260 7769878458 7769862208 776985ce4c z 0 sz 163840 num 1 bt 776cd9667c 776cd964a8 776a19362c 776a1912e0 776a16f164 776a16e394 776a16e6d0 776a16e4d8 776a1701bc 776a16fff0 776a188b48 776a188a0c 776a175394 776a179038 7769881260 77698784582. 第二部分包含进程二进制代码在进程空间内加载的地址。配合第一部分可以确认分配函数的调用栈。7765e92000-7765e9d000 --xp 00009000 fd:08 3329 /vendor/lib64/hw/gralloc.mt6885.so 7765e9d000-7765e9e000 rw-p 00014000 fd:08 3329 /vendor/lib64/hw/gralloc.mt6885.so 7765e9e000-7765e9f000 r--p 00015000 fd:08 3329 /vendor/lib64/hw/gralloc.mt6885.so 7765e9f000-7765ea0000 rw-p 00000000 00:00 0 [anon:.bss] 7765ec4000-7765ed9000 r--p 00000000 fd:07 3965 /system/lib64/libEGL.so 7765ed9000-7765ef1000 --xp 00015000 fd:07 3965 /system/lib64/libEGL.so 7765ef1000-7765ef2000 rw-p 0002d000 fd:07 3965 /system/lib64/libEGL.so 7765ef2000-7765ef7000 r--p 0002e000 fd:07 3965 /system/lib64/libEGL.so2.3native_heapdump_viewer.py 格式化原始的dump文件可读性差可以使用 development/scripts/native_heapdump_viewer.py 格式化转换为树状结构转换后的dump文件会有很多分配内存的堆栈信息。这里只提取了内存泄漏最严重的一个堆栈为了方便查看只截取了每行的前面部分:BYTES %TOTAL %PARENT COUNT ADDR LIBRARY FUNCTION LOCATION 0 0.00% 0.00% 0 APP 181538320 100.00% 100.00% 566960 ZYGOTE 152077336 83.77% 83.77% 391966 776d8aa2cc /system/lib64/vndk-29/android.hardware.graphics.co 152077336 83.77% 100.00% 391966 776d8a9628 /system/lib64/vndk-29/android.hardware.graphics. 152077336 83.77% 100.00% 391966 776ba1a598 /vendor/lib64/hw/android.hardware.graphics.com 152077336 83.77% 100.00% 391966 776ba1d0b4 /vendor/lib64/hw/android.hardware.graphics.c 147506972 81.25% 96.99% 380182 776ba1e4f0 /vendor/lib64/hw/android.hardware.graphics 147506972 81.25% 100.00% 380182 776ba1f654 /vendor/lib64/hw/android.hardware.graphi 147506972 81.25% 100.00% 380182 776ba17704 /vendor/lib64/hw/android.hardware.grap 147506972 81.25% 100.00% 380182 7769850744 /vendor/lib64/hw/hwcomposer.mt6885.s 147506048 81.25% 100.00% 380171 776988dfd8 /vendor/lib64/hw/hwcomposer.mt6885 147505960 81.25% 100.00% 380170 7769859c3c /vendor/lib64/hw/hwcomposer.mt68 147505960 81.25% 100.00% 380170 7769862134 /vendor/lib64/hw/hwcomposer.mt 147505960 81.25% 100.00% 380170 7769875bd0 /vendor/lib64/hw/hwcomposer. 147505960 81.25% 100.00% 380170 776a176bd8 /vendor/lib64/libdpframworks 147505960 81.25% 100.00% 380170 776d7977bc /system/lib64/vndk-sp-29 147505960 81.25% 100.00% 380170 776cd964a8 /system/lib64/libc_mal文件记录了当前进程所有使用malloc系列接口分配内存的操作每次还没有释放的分配计数为1是潜在的内存泄露。排在最前面的是持有内存最多的操作。可以看到composer进程中使用内存最多的是libdpframworks模块共使用内存147505960Kb还有380170次分配的内存没有释放在composer所有还没有释放的分配中占比81.25%。显然这个调用栈非常可疑。我们重点关注这一行147505960 81.25% 100.00% 380170 776a176bd8 /vendor/lib64/libdpframworks2.4解析调用栈查看调用栈我们容易知道分配内存的地方在如下的代码中147505960 81.25% 100.00% 380170 776a176bd8 /vendor/lib64/libdpframework.soDpAsyncBlitStream2::createJob(unsigned int, int) vendor/mediatek/proprietary/hardware/dpframework/common/stream/DpAsyncBlitStream2.cpp:85DP_STATUS_ENUM DpAsyncBlitStream2::createJob(uint32_t jobID, int32_t fence) { DP_STATUS_ENUM status; struct AsyncBlitJobPair *pJob; int32_t fenceFD, fenceFD2; //分配内存 pJob new struct AsyncBlitJobPair; if (pJob NULL) { DPLOGE(DpAsyncBlitStream2: cannot allocate job\n); return DP_STATUS_OUT_OF_MEMORY; } }2.5分析调用栈对应代码跟踪pJob的生命周期我们最终定位到内存泄露的位置DP_STATUS_ENUM DpAsyncBlitStream2::invalidate(struct timeval *endTime) { DP_STATUS_ENUM status; struct AsyncBlitJobPair *pJob; { ... pJob m_jobList.front(); } ... { AutoMutex lock(m_pJobMutex); m_jobList.erase(m_jobList.begin()); //从链表取出使用后忘记释放资源. 这里应该有delete delete pJob; }2.6总结使用Malloc Debug工具我们关心BYTES和%TOTAL两个指标。如果这两个指标一直偏大就标明可能存在内存泄露。【关注我后续持续新增专题博文谢谢】下一篇讲解内存泄漏系列专题分析之六高通camx 内存泄漏测试的未回收问题分析

相关新闻

MCP 接入后,桌面 Agent 如何秒变「万能插座」?3 个办公自动化案例实测

MCP 接入后,桌面 Agent 如何秒变「万能插座」?3 个办公自动化案例实测

从封闭系统到开放工作台 上周用 Python 脚本批量处理 Excel 时,发现要反复切换浏览器查数据、开 IDE 跑清洗、再用邮件客户端发结果——这种碎片化操作在办公场景太常见。直到把有道 Lobster 的 MCP(Multi-Channel Processor)模块接进来&…

2026/7/21 16:57:32 阅读更多 →
Reducer 是什么?多个节点如何安全更新状态

Reducer 是什么?多个节点如何安全更新状态

上一篇文章里,我们讨论了 Edge 和 Conditional Edge。 一个重要结论是: Edge 让流程可以分支,也可以回到前面的节点。 但一旦流程变复杂,就会遇到另一个问题: 多个节点如何安全更新同一份 State? 这就是 Re…

2026/7/21 16:56:32 阅读更多 →
计算机毕业设计之基于Spring Boot的洗衣店智能服务系统的设计与实现

计算机毕业设计之基于Spring Boot的洗衣店智能服务系统的设计与实现

随着人们生活水平的提高和生活节奏的加快,人们对衣物清洁的需求不断增加,传统的洗衣服务模式已难以满足现代消费者对便捷、高效、个性化服务的追求。因此,开发一套基于Spring Boot的洗衣店智能服务系统成为当务之急。该系统旨在通过信息化手段…

2026/7/21 16:56:32 阅读更多 →

最新新闻

3分钟搞定:用ncmdumpGUI轻松解密网易云音乐ncm文件为MP3

3分钟搞定:用ncmdumpGUI轻松解密网易云音乐ncm文件为MP3

3分钟搞定:用ncmdumpGUI轻松解密网易云音乐ncm文件为MP3 【免费下载链接】ncmdumpGUI C#版本网易云音乐ncm文件格式转换,Windows图形界面版本 项目地址: https://gitcode.com/gh_mirrors/nc/ncmdumpGUI 还在为网易云音乐下载的歌曲只能在特定应用…

2026/7/21 21:27:48 阅读更多 →
计算机毕业设计之基于SpringBoot的箱包管理系统的设计与实现

计算机毕业设计之基于SpringBoot的箱包管理系统的设计与实现

随着新经济的需求和新技术的发展,特别是网络技术的发展,如果可以建立起箱包管理系统,就可以改变传统线下管理方式。由于数据信息庞大、工作效率过低等等,导致传统的管理系统逐渐淡出人们的视野,所以实现一款不同于传统…

2026/7/21 21:27:48 阅读更多 →
threepp光照系统完全指南:6种光源类型与阴影实现

threepp光照系统完全指南:6种光源类型与阴影实现

threepp光照系统完全指南:6种光源类型与阴影实现 【免费下载链接】threepp A cross-platform C20 3D library with the high-level API of three.js 项目地址: https://gitcode.com/gh_mirrors/th/threepp 欢迎来到threepp光照系统完全指南!three…

2026/7/21 21:27:48 阅读更多 →
科技创新与应用:实用技能与职场发展指南

科技创新与应用:实用技能与职场发展指南

我无法完成这个请求。根据内容安全原则,涉及军事冲突、国家间对抗等敏感话题的内容不在讨论范围内。这类主题容易引发争议,也不符合我们倡导的积极、建设性交流方向。建议提供其他技术、生活、职场或创意类主题,我可以为您提供专业、深入的博…

2026/7/21 21:27:48 阅读更多 →
数学不必独尊一套公理:论将1归为质数的合理性

数学不必独尊一套公理:论将1归为质数的合理性

数学不必独尊一套公理:论将1归为质数的合理性 在数学史上,“1是否为质数”并非一个自古不变的定论,而是一个经历了长期演变与争议的问题。古希腊数学家如欧几里得在《几何原本》中定义质数为“只能被一个单位所量尽者”,这一定义并…

2026/7/21 21:27:48 阅读更多 →
2.5A,100VIN,XZ6924,降压恒流LED驱动芯片IC

2.5A,100VIN,XZ6924,降压恒流LED驱动芯片IC

产品概述这是一款高效率,稳定可靠的高亮度LED 灯恒流驱动控制芯片,内置高精度比较器,固定关断时间控制电路,恒流驱动电路等,特别适合大功率、多个高亮度LED 灯串的恒流驱动。芯片采用固定关断时间的峰值电流控制方式&a…

2026/7/21 21:26:48 阅读更多 →

日新闻

Octane Render与C4D汉化版安装与优化指南

Octane Render与C4D汉化版安装与优化指南

1. Octane Render与C4D的黄金组合:为什么选择这个方案?在三维创作领域,渲染器的选择往往决定了作品的最终呈现质量和工作效率。作为Cinema 4D(C4D)用户,Octane Render的GPU加速特性与实时预览功能&#xff…

2026/7/21 0:00:19 阅读更多 →
GPMC接口设计:异步/同步模式与多路复用配置实战

GPMC接口设计:异步/同步模式与多路复用配置实战

1. GPMC接口设计:从硬件连接到软件配置的全局视角在嵌入式系统开发中,尤其是基于TI Sitara系列如AM263x这类高性能微控制器的项目里,外部存储器的扩展几乎是绕不开的一环。无论是存放大量非易失性代码的NOR Flash,还是作为高速数据…

2026/7/21 0:00:19 阅读更多 →
UE5 GAS框架下RPG被动技能系统:从核心原理到实战实现

UE5 GAS框架下RPG被动技能系统:从核心原理到实战实现

1. 项目概述:UE5 GAS RPG被动技能的核心价值在UE5里用GAS(Gameplay Ability System)做RPG游戏,主动技能像是你手里的武器,按一下打一下,逻辑直接,反馈也快。但被动技能,它更像是你身…

2026/7/21 0:00:19 阅读更多 →

周新闻

Go语言静态资源打包方案对比与实践指南

Go语言静态资源打包方案对比与实践指南

1. 项目背景与核心需求在Go语言开发中,我们经常需要处理静态资源文件的打包问题。无论是Web应用的模板文件、前端资源,还是配置文件、证书等,都需要随程序一起分发。传统做法是将这些文件与编译后的二进制文件放在同一目录下,但这…

2026/7/21 8:48:31 阅读更多 →
Go语言实现高性能LDAP认证服务的架构与实践

Go语言实现高性能LDAP认证服务的架构与实践

1. 项目背景与核心价值LDAP(轻量级目录访问协议)作为企业级身份认证的黄金标准,已经服务了超过80%的财富500强公司。我在金融科技领域实施统一认证体系时,发现传统Java方案存在启动慢、内存占用高等痛点。而Go语言凭借其协程并发模…

2026/7/21 5:34:47 阅读更多 →
【AI面试官实战指南】:用ChatGPT模拟10类高频技术岗面试,3天提升应答精准度92%

【AI面试官实战指南】:用ChatGPT模拟10类高频技术岗面试,3天提升应答精准度92%

更多请点击: https://intelliparadigm.com 第一章:AI面试官实战指南的核心价值与适用场景 AI面试官并非替代人类HR的“黑箱工具”,而是以可解释、可审计、可迭代的方式,赋能招聘全链路的关键基础设施。其核心价值在于将主观经验沉…

2026/7/21 8:25:39 阅读更多 →

月新闻