美文网首页android干货分享
android native 代码内存泄露 定位方案

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

作者: 代码GG陆晓明 | 来源:发表于2017-06-21 21:44 被阅读565次

    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.propecho "libc.debug.malloc.options=backtrace" >> /system/build.propecho "libc.debug.malloc.env_enabled=1" >> /system/build.propecho "libc.debug.malloc=1" >> /system/build.prop将我们编译出来的libc.so libstdc++.so 放入手机
    adb remountadb push 'xxxxxx/system/lib/libc.so' /system/libadb push xxxxxx/system/lib/libstdc++.so' /system/libadb reboot然后我们重启手机,运行我们的测试demo,这里为jnidemo我们如何来验证是否成功的debug malloc呢?mt:/ # ps | grep jnidemou0_a155 13112 343 821920 45124 SyS_epoll_ a695c8a8 S com.example.jnidemomt:/ # cat /proc/13112/maps | grep malloc_debuga718a000-a71ae000 r-xp 00000000 103:08 1400 /system/lib/libc_malloc_debug.soa71af000-a71b0000 r--p 00024000 103:08 1400 /system/lib/libc_malloc_debug.soa71b0000-a71b1000 rw-p 00025000 103:08 1400 /system/lib/libc_malloc_debug.somt:/ # 如果出现了这个/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

    相关文章

      网友评论

      本文标题:android native 代码内存泄露 定位方案

      本文链接:https://www.haomeiwen.com/subject/puezqxtx.html