前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >android native 代码内存泄露 定位方案

android native 代码内存泄露 定位方案

作者头像
用户1263308
发布2018-02-02 10:32:25
4.7K1
发布2018-02-02 10:32:25
举报
文章被收录于专栏:代码GG之家代码GG之家

android native 代码内存泄露 定位方案

java代码的内存定位,暂时我们先不关注。此篇文章,主要围绕c c++代码的内存泄露。

欢迎留言,交流您所使用的内存泄露定位方案。 c c++代码,由于其特殊性质,没有虚拟机概念,内存则直接是由用户管理,比如申请,释放,都是需要用户主动去触发,如果用户出现使用了申请,但是用完之后,没有调用释放,则会引起内存泄露。这种叫真正意义的内存泄露,只有重启机子,才能恢复。

相对而已java端的内存泄露,指的是一个应用长期运行,导致相互引用,无法释放,GC没法回收,引起的有效内存越来越小,我们将此现象叫做,内存泄露,通过关闭此应用,重新打开即可恢复内存。因此看来,java内存泄露和c c ++ 的 还是有本质区别的。

java本身的虚拟机里面会关注对象的申请,释放,这些不需要用户直接注,java虚拟机通过管理机制,将调用c c++里面真正的malloc free 方法,封装起来,将java对象的生命周期和malloc free 进行关联,则可以保证在对象不使用的时候,内存紧张时,释放掉不再被引用的对象,GC回收就是在做这件事请。 回到我们这节的主要内容,如何定位我们的c c++的内存泄露。

00

我们查看代码,发现申请内存的代码位置,在/bionic/libc/里面,此库生成出来有 libc.so libstdc++.so (手机的system/lib/里面)我们看到这里有个目录/bionic/libc/malloc_debug/ ,这里便是我们的malloc的调试源码 按照这里的文档讲解 (有个重点,必须是调试版本,因为需要lib_malloc_debug.so 库的存在)

此malloc的调试原理是:当系统发现我们有libc.debug.malloc的一些列配置成立时,此时系统会将malloc free 等方法,重新指向到 lib_malloc_debug.so里面的对应实现方法,lib_malloc_debug.so里面的方法,像比较而言,多了一些记录信息,将每次的申请时的地址,堆栈,so等信息记录下来,然后我们需要的时候,则通过工具ddms dump出来,进行分析每个申请的内存,是否正常的释放了,是否出现了内存泄露。

官方的方案: adb shell stop adb shell setprop libc.debug.malloc.options backtrace adb shell start 然后打开需要检测的apk,然后运行,调试下即可出来。实际是不可行的。。。如此尴尬,我也不想太过麻烦的去找why?我们何不暴力一些,直接修改下,这里我给出我的修改方法:

/bionic/libc/bionic/malloc_common.cpp 修改 static void malloc_init_impl(libc_globals* globals) 方法,将前面的一些判断删掉

然后在/bionic/libc目录 mmm单独编译此模块出来即可

adb shell 加入一些参数 (没去较真,是否需要这个) echo "libc.debug.malloc.program=app_process">> /system/build.prop echo "libc.debug.malloc.options=backtrace" >> /system/build.prop echo "libc.debug.malloc.env_enabled=1" >> /system/build.prop echo "libc.debug.malloc=1" >> /system/build.prop 将我们编译出来的libc.so libstdc++.so 放入手机

adb remount adb push 'xxxxxx/system/lib/libc.so' /system/lib adb push xxxxxx/system/lib/libstdc++.so' /system/lib adb reboot 然后我们重启手机,运行我们的测试demo,这里为jnidemo 我们如何来验证是否成功的debug malloc呢? mt:/ # ps | grep jnidemo u0_a155 13112 343 821920 45124 SyS_epoll_ a695c8a8 S com.example.jnidemo mt:/ # cat /proc/13112/maps | grep malloc_debug a718a000-a71ae000 r-xp 00000000 103:08 1400 /system/lib/libc_malloc_debug.so a71af000-a71b0000 r--p 00024000 103:08 1400 /system/lib/libc_malloc_debug.so a71b0000-a71b1000 rw-p 00025000 103:08 1400 /system/lib/libc_malloc_debug.so mt:/ # 如果出现了这个/system/lib/libc_malloc_debug.so 则说明内存检测调试成功了。 我们继续来操作,找到我们电脑home目录下的隐藏文件 /home/user/.android 在里面的ddms.cfg文件下加入一行 native=true 加入这句之后,我们的eclipse的独立ddms则会出现native heap显示。 找到eclipse的sdk目录下的/sdk/tools 里面的ddms打开。(记得要关闭其他占用adb的端口进程)

选择我们的进程,然后右侧选择native heap ,点击snapshot current native Heap usage,则会显示出来当前进程申请的c内存信息。

我们通过这个,可以看到某个库的某个位置申请了多大空间,然后我们多次操作,对比申请的空间,从而找到我们的内存泄露点。

01

我们这里演示下如何找到对应的申请空间代码位置: 从上面的图可以看到libtest_jni.so的位置,方法位置8cf84c54 申请大小为100 于是我们需要找下8cf84c54 具体指的哪个方法,具体操作为: 使用cat /proc/pid/maps | grep libtest_jni.so

看到8cf84c54 落在这里的地址为8cf84000 - 8cf87000 中间,再看后面的r-xp 这里有执行位。p私有位 于是我们计算8cf84c54(代码的具体位置) -8cf84000(so的加载起始位置) =0xc54(so库中对应方法的地址) 我们使用addr2line找到这个地址的代码,这里可以看到是XXXXXXXtest_jni.c 的13行,具体为,还是这个图:

找到代码:

我们这里看到 malloc 申请的大小为 100字节 代码位置为13行,我们一直在申请,没有释放过,如上验证了c c++ 内存问题,可以通过此方案进行调试,定位内存泄露问题。

02

综上演示了一次查找,定位c代码申请空间的位置代码,如果发现某个过程的meminfo信息出来的native heap一直增大,我们则可以使用这个调试手段,进行定位,一般尽量定位在跟自己app关联比较大的方法里面。 这里有个小问题,按照ddms这个工具的本身意图,当我们配置好addr2line之后,配置好符号查找位置后,应该自动会解析成符号,而不是地址。但是这里老是提示addr2line工具找不到,很是崩溃,无语,所以才有了上面的手动解析地址到方法的手段。 不过话说回来,这样子不是更学到了内容,还是值得高兴的事情。

一些参考文档:

java内存泄露

http://www.open-open.com/lib/view/open1432102426271.html

http://www.open-open.com/lib/view/open1425993728107.html

LeakCanary

http://www.open-open.com/lib/view/open1453976808761.html

native内存泄露

http://blog.csdn.net/u011280717/article/details/51820268

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2017-06-20,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 代码GG之家 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • LeakCanary
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档