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

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

原文发布于微信公众号 - 代码GG之家(code_gg_home)

原文发表时间:2017-06-20

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏大壮

iOS GCD定时器

2683
来自专栏卡少编程之旅

使用HEXO搭建个人博客遇到的几个问题

36613
来自专栏后端技术探索

Nginx服务器架构简析

模块化结构的思想是一个很久的概念,但也正是成熟的思想造就了Nginx的巨大优越性。

943
来自专栏JavaEE

nginx+vsftp搭建图片服务器前言:简介:nginx的安装:vsftp的安装:配置nginx为图片服务器:总结:

6295
来自专栏前端小叙

win10系统下cmd输入一下安装的软件命令提示拒绝访问解决办法

问题:win10系统安装了nvm,cmd命令提示不是内部或外部命令 解决:卸载nvm,重新安装,再一次输入nvm,发现正常显示: ? 问题:win10安装了nv...

3974
来自专栏Java成神之路

Idea_学习_09_Idea 方法自动生成参数默认名

生成方法后,还空着参数,可以使用 ctrl + alt + 空格 ,列出参数,然后选择参数即可。

1094
来自专栏好好学习吧

testng执行多个suite

由于testng.xml中只能设置一个<suite>标签,就无法创建多个测试集,通过<suite-files >标签可以实现允许多个测试集。

3361
来自专栏吴伟祥

Redis实现消息队列 转

打开浏览器,输入地址,按下回车,打开了页面。于是一个HTTP请求(request)就由客户端发送到服务器,服务器处理请求,返回响应(response)内容。

1851
来自专栏Java进阶

再谈session 和 cookie的差异

2708
来自专栏遊俠扎彪

inux (CentOS 5.6) 安装 Sun JDK (切换自带OpenJDK)

可根据需要卸载不需要的JDK Version

1978

扫码关注云+社区