Android native memory leak detect (Android native内存泄露检测)
2020年12月08日16:56:51更新
准备搞一个类似malloc debug的东西,监控系统的内存申请与释放,最后导出申请了但是未释放部分的代码,有兴趣的一起开发:传送门
简介
Android应用中,经常会有业务需要使用到Native实现。比如加密,音视频播放等。也就是常见的二进制文件xxx.so
这部分代码,申请的内存不走Java Heap管理。那么一旦发生内存泄露,无法使用导出MAT来进行查看。
本篇文章将讲解如何使用Google霸霸提供的方法Malloc Debug
,定位Native内存泄露问题。
关于wrap shell脚本
在Android 8.1
及以后的系统里,Google允许应用程序开发者通过wrap.sh脚本文件,来设置Native代码运行时环境。这个wrap脚本有多个功能,本篇文章只讲解其中一个,使用malloc debug
追踪Native内存申请,进而定位到Native内存泄露。
切记,是在
Android 8.1
及以后的系统才具备该特性。 当然Android 8.0
系统也可以,但是需要Root,且比较麻烦,设备必须切换到宽容模式
编写自己的wrap shell脚本
#!/system/bin/sh
LIBC_DEBUG_MALLOC_OPTIONS=backtrace $@
这里的backtrace
代表打开malloc debug
功能,保存为wrap.sh,并赋予可执行权限。
关于wrap.sh
这里不详细介绍,详情看考封装 Shell 脚本(wrap.sh)
配置自己的应用
- 准备好了
wrap.sh
之后,需要将其放在自己的应用程序中,需要新建一个用于存放脚本的目录结构,与so目录一一对应。放好之后的应用目录结构如下:
|- src|- main|- jniLibs|- x86|- libhello-jni.so|- arm64-v8a|- libhello-jni.so|- 其他指令集对应的目录|- shell|- lib|- x86|- wrap.sh|- arm64-v8a|- wrap.sh|- 其他指令集对应的目录
上面省略了部分指令集的目录,根据自己的应用适配的指令集添加或者删除对应的目录。每一个so指令集都需要拷贝一份wrap.sh
到对应的shell目录下。
紧接着,build.gradle
文件下应该要有如下配置:
android {// 其他配置sourceSets {main {jniLibs {srcDir {"src/main/jniLibs"}}resources {srcDir {"src/main/shell"}}}}
}
jniLibs
是指定so路径,resources
是指定shell脚本的路径。
编译Debug包并安装运行
运行Debug APK
,使用,并复现Native内存泄露的场景,Native内存占用多少,可以使用Android Studio
进行查看。
如果希望在Release
包下也开启调试,可以在AndroidManifest.xml
文件下的<application>
标签下添加android:debuggable="true"
,但是,需要注意,应用正式上线的时候,需要去掉,否则后果自负!!!!
经过上面这些步骤打出来的apk,可以看到lib
目录下会有shell脚本:
导出内存申请调用栈信息
在程序发生Native内存泄露之后,通过下面的命令可以导出内存申请的调用栈信息
adb shell am dumpheap -n <PID_TO_DUMP> /data/local/tmp/heap.txt
其中<PID_TO_DUMP>
是你的应用进程对应的PID
紧接着,把heap.txt
从手机里导出来
adb pull /data/local/tmp/heap.txt .
导出后的heap.txt
需要进行转换才能查看,google提供了转换的python脚本文件native_heapdump_viewer.py
我这里给的脚本链接,是没问题的版本,google最新提交的一个版本无法跑通。
修改脚本
如果你是Mac/windows
,需要修改下载好的脚本,用文本编辑器打开,修改两处。
第一处:
result = subprocess.check_output(["objdump", "-w", "-j", ".text", "-h", sofile])
将这里的objdump
改成NDK路径下的对应路径:
/Users/liangqiu/android/ndk/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64/bin/arm-linux-androideabi-objdump
第二处:
p = subprocess.Popen(["addr2line", "-C", "-j", ".text", "-e", sofile, "-f"], stdout=subprocess.PIPE, stdin=subprocess.PIPE)
将这里的addr2line
改成NDK路径下的对应路径:
/Users/liangqiu/android/ndk/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64/bin/arm-linux-androideabi-addr2line
改好python脚本后,接着执行脚本导出内存泄露调用栈数据:
python development/scripts/native_heapdump_viewer.py --symbols ./symbols/ heap.txt > heap_info.txt
注意这里的./symbols/
参数,该参数指定的是带符号表so所在目录。
开发过NDK动态库的都应该清楚,需要指定带符号表的so才能解析出函数名字,这里的--symbols
参数就是需要指定的带符号so所在的目录。
特别提醒,这里的带符号表so路径是有要求的
比如我们开发了一个libtest.so
动态库,需要对其泄露方法调用栈进行解析。
假设我们的应用程序安装到手机
后libtest.so
所在路径为/data/app/com.mydemo.demo/lib/arm/libtest.so
,如下
|- data|- app|- com.mydemo.demo|- lib|- arm|- libtest.so
那么我们就需要在./symbols/
目录下建立以下层级的目录结构/data/app/com.mydemo.demo/lib/arm/
并且把带符号表的libtest.so
放在arm
目录下。建立好之后,带符号so路径应该为./symbols/data/app/com.mydemo.demo/lib/arm/libtest.so
,如下:
|- symbols|- data|- app|- com.mydemo.demo|- lib|- arm|- libtest.so
传递给脚本的--symbols
参数为./symbols/
即可。脚本将根据heap.txt
中动态库libtest.so
的路径在./symbols/
目录下,以相对路径找到带符号表so文件,并解析函数调用栈。
接着,打开生成的heap_info.txt
文件,就能看到所有so,每个方法申请的内存信息,举个栗子
5831776 29.09% 100.00% 10532 71b07bc0b0 /system/lib64/libandroid_runtime.so Typeface_createFromArray frameworks/base/core/jni/android/graphics/Typeface.cpp:68
引用官方的解释
5831776 is the total number of bytes allocated at this stack frame, whichis 29.09% of the total number of bytes allocated and 100.00% of the parentframe's bytes allocated. 10532 is the total number of allocations at thisstack frame. 71b07bc0b0 is the address of the stack frame.
到这,搜索一下自己的应用包名,看看内存占整体的百分比,找到泄露内存多的,so,对应能看到申请内存的行数。
demo项目
见github仓库
总结
- Google在8.1及以后,才允许程序开发人员通过
wrap.sh
来配置自己的程序进程二进制运行环境。起步有点晚,相对ios。 - 其实对于Rom开发人员,有更便捷的方式,具体参考Rom开发的同学看过来
Native内存泄露连接总结
- Google官方对
Malloc Debug
工具说明文档:mallock debug wrap.sh
脚本文件说明:wrap.shnative_heapdump_viewer.py
脚本最新版地址(不一定能用,可能需要自己进行修改)- 我测试可用的
native_heapdump_viewer.py
脚本地址
更多的Native调试工具?
Google官方:
https://source.android.com/devices/tech/debug/native-memory
没错,这个是Google为Android平台Native问题排查提供的方法引导,如果这里没有你想要的,那其他路径怕是也找不到了,如果有,请兄弟们分享出来一块学习。
问题
描述
在Demo中,一切运行正常。然鹅,放入自己的项目里之后,发现很快就报错了:
07-16 17:18:58.453 10422-10422/? A/DEBUG: *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
Build fingerprint: 'google/taimen/taimen:8.1.0/OPM4.171019.021.R1/4833808:user/release-keys'
Revision: 'rev_10'
ABI: 'arm'
pid: 10391, tid: 10391, name: immomo.momo.dev >>> com.immomo.momo.dev <<<
signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0xffffea8cr0 00000001 r1 00000008 r2 ffffea8c r3 00000004r4 00000008 r5 ffadf168 r6 00000000 r7 ffadec54r8 ffadec2c r9 00000ff0 sl 00000000 fp ffadf8fcip 00000008 sp ffadeaf8 lr d5ed58ec pc d5ed5030 cpsr 000f0010
07-16 17:18:58.462 10422-10422/? A/DEBUG: backtrace:#00 pc 00037030 /data/app/com.immomo.momo.dev-cMfhOMn7a0CDUJybWWvv9A==/lib/arm/libmoleveldb.so (_Unwind_VRS_Pop+84)#01 pc 000378e8 /data/app/com.immomo.momo.dev-cMfhOMn7a0CDUJybWWvv9A==/lib/arm/libmoleveldb.so (__gnu_unwind_execute+876)#02 pc 00037938 /data/app/com.immomo.momo.dev-cMfhOMn7a0CDUJybWWvv9A==/lib/arm/libmoleveldb.so (__gnu_unwind_frame+52)#03 pc 00029559 /data/app/com.immomo.momo.dev-cMfhOMn7a0CDUJybWWvv9A==/lib/arm/libmoleveldb.so#04 pc 0002070b /system/lib/libc_malloc_debug.so (_Unwind_Backtrace+146)#05 pc 00007ec9 /system/lib/libc_malloc_debug.so (backtrace_get(unsigned int*, unsigned int)+28)#06 pc 000059b7 /system/lib/libc_malloc_debug.so (InitHeader(Header*, void*, unsigned int)+194)
07-16 17:18:58.463 10422-10422/? A/DEBUG: #07 pc 0000556b /system/lib/libc_malloc_debug.so (internal_malloc(unsigned int)+82)#08 pc 000054d1 /system/lib/libc_malloc_debug.so (debug_malloc+48)#09 pc 00024a63 /data/app/com.immomo.momo.dev-cMfhOMn7a0CDUJybWWvv9A==/lib/arm/libmoleveldb.so (operator new(unsigned int)+22)#10 pc 00024a9b /data/app/com.immomo.momo.dev-cMfhOMn7a0CDUJybWWvv9A==/lib/arm/libmoleveldb.so (operator new[](unsigned int)+2)#11 pc 00021d9b /data/app/com.immomo.momo.dev-cMfhOMn7a0CDUJybWWvv9A==/lib/arm/libmoleveldb.so (leveldb::Status::Status(leveldb::Status::Code, leveldb::Slice const&, leveldb::Slice const&)+34)#12 pc 00023f9f /data/app/com.immomo.momo.dev-cMfhOMn7a0CDUJybWWvv9A==/lib/arm/libmoleveldb.so
大致信息是说,当业务模块通过malloc
申请内存时,会走到代理的/system/lib/libc_malloc_debug.so
里的debug_malloc
方法,接着该方法通过libunwind
模块来获取调用栈,此时报错了。具体的报错分析,参考大神博客
解决方案
一种方案是等待Google更新新系统之后解决,然鹅,解决不知道是什么时候的事,所以参照大神的思路,自己编译Android固件并刷入手机。不过得修改libunwind llvm
模块的UnwindLevel1-gcc-ext.c
文件(参考大神git),这样,生成的/system/lib/libc_malloc_debug.so
便可以正常获取调用栈信息。
这里我把 32位libc_malloc_debug.so 以及64位libc_malloc_debug.so保留下来(Android 8.1)供大家使用,如果手上有Android 8.1的root手机,可以用我这个so文件替换/system/lib
以及system/lib64
下的对应文件,即可正常跑起来Malloc Debug
。
这里为何选择编译Android 8.1而不是Android 5/6/7是因为Android 8.0及以上才能够针对某一个进程开启Malloc Debug功能,低版本只能针对所有进程开启,会导致手机非常卡。
2019年11月07日11:27:51更新
为了方便大家使用,将自己写的替换导出脚本以及GUI程序开源到github方便大家使用。
传送门
尊重原创,转载请注明出处:https://blog.csdn.net/a740169405/article/details/81032228
Android native memory leak detect (Android native内存泄露检测)相关推荐
- Android系统(31)--- 如何分析native memory leak
如何分析native memory leak 分析native程序发生内存泄漏问题,就需要对每一笔内存申请做记录,释放内存时清除记录,然后在认为存在内存泄漏时将记录提取出来分析,看看在哪段代码存在申请 ...
- Android 系统(30)---如何用DDMS分析native memory leak
native程序如果发生内存问题,一般都比较难查,幸好DDMS有集成native memory leak(仅仅针对app,无法分析mediaserver等非app的进程)功能,通过DDMS可以观察na ...
- android native堆内存泄露,Android Native内存泄露检测
Android Studio没有提供直接的Native层的内存泄露检测工具,但我们仍可以通过开源工具进行动态检测和静态检测 动态检测 在APP运行时进行检测,就像LeakCanary Update: ...
- Android NDK 内存泄露检测
前言 最近写C++代码,老是担心代码存在内存泄露,胆战心惊的,Andorid中Java层代码内存泄露可以借助leakcanary进行检测:找了一番,找到了PC上C++上的内存泄露检测库LeakTrac ...
- SQL Server 内存泄露(memory leak)——游标导致的内存问题
原文:SQL Server 内存泄露(memory leak)--游标导致的内存问题 转自:http://blogs.msdn.com/b/apgcdsd/archive/2011/07/01/sql ...
- memwatch内存泄露检测工具
工具介绍 官网 http://www.linkdata.se/sourcecode/memwatch/ 其功能如下官网介绍,挑选重点整理: 1. 号称功能: 内存泄露检测 (检测未释放内存, 即 动态 ...
- DevPartner Studio Professional Edition 11 内存泄露检测使用
分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow 也欢迎大家转载本篇文章.分享知识,造福人民,实现我们中华民族伟大复兴! DevP ...
- C语言内存泄露检测--Memwatch
项目:迅雷下载库内存泄露检测 作者:曾金龙 供职:深圳迅雷网络技术股份有限公司 领域:迅雷下载库 时间:2014-07-26 迅雷的移动下载库是用C语言编写的,为了能够横跨欧亚非拉(ios,andro ...
- Ubuntu下内存泄露检测工具Valgrind的使用
在VS中可以用VLD检测是否有内存泄露,可以参考http://blog.csdn.net/fengbingchun/article/details/44195959,下面介绍下Ubuntu中内存泄露检 ...
- 精准 iOS 内存泄露检测工具
MLeaksFinder:精准 iOS 内存泄露检测工具 发表于 2016-02-22 | zepo | 23 Comments 背景 平常我们都会用 Instrument 的 Lea ...
最新文章
- SQL--添加角色权限
- ofbiz之entity 实体解析 扩展 视图 复合列写法
- python运行错误-Python在运行中发生错误怎么正确处理方法,案例详解!
- 【Oracle】PL/SQL Developer使用技巧(持续更新中)
- SkinMagic使用后按钮加自定义图标或菜单GetMneu返回NULL的解决方法
- memcached mysql缓存_memcached做数据库缓存
- 数据结构与算法(C#版)第二章 C#语言与面向对象技术(中)V1.0
- 【Python CheckiO 题解】Secret Message
- android l 效果,[原]Android L中水波纹点击效果的实现
- Go Web 编程--超详细的模板库应用指南
- google浏览器不能登录
- Windows C盘格式化或者同平台迁移oracle数据库
- mysql sum 对应_ASK MySQL查询SUM不同的表
- Python自动化之操作PPT--更新模板数据,减少重复操作
- JavaScript替换字符串中括号里的内容
- pyCharm最新2018.2激活 附激活文件/教育版免费使用
- About Config
- 解决ERROR: text file '***' contains disallowed UTF-8 whitespace character(s)
- JAVA实验七 图形用户界面的设计与实现
- Deferred Decal(延迟贴花)
热门文章
- xshell与虚拟机VMware中centos6.7系统突然连不上
- λ^n |λI−AB|= λ^m |λI−BA|
- python docx 表格样式修改 Package not found at ‘*.docx‘; “no style with name ‘Table Grid‘“
- adc分辨率和精度的区别_STM32学习笔记—ADC采集数据常见问题
- 使用Gotestwaf测试WAF检测能力
- 巨牛,访问github速度加快数倍
- 133个Java面试问题列表
- Android 内存监测工具 DDMS -- Heap
- 数独游戏的设计与实现
- android API Guides学习--Introduction(1)