native内存泄漏分析

16 篇文章 0 订阅
16 篇文章 0 订阅

一.摘要

    我们在分析内存泄漏时java内存泄漏我们可以集成LeakCanary来进行监控,出现问题时会打印出泄漏时的引用关系,那么我们native内存泄漏时如何分析呢?native内存泄漏我们可以通过malloc_debug工具来进行监控,详细的介绍请参考:

https://android.googlesource.com/platform/bionic/+/master/libc/malloc_debug/README.md

 

二.如何使用

    默认情况下高通的手机已经包含了malloc_debug的代码,具体路径在:bionic/libc/malloc_debug/,下边介绍使用方法:

1.使用dumpsys meminfo --unreachable分析

1.1设置你要监控的进程:

adb shell setprop wrap.<APP>  '"LIBC_DEBUG_MALLOC_OPTIONS=backtrace"'

例如:

adb shell setprop wrap.com.smartisanos.security '"LIBC_DEBUG_MALLOC_OPTIONS=backtrace"'

adb shell am force-stop com.smartisanos.security

1.2重启进程:

设置完属性后需要重启进程,我们先杀死进程adb shell am force-stop <APP>

如果之前的设置正常,进程重启后你会发现你进程的父进程并不是zygote64,而是:

sh -c LIBC_DEBUG_MALLOC_OPTIONS=backtrace /system/bin/app_process64 /system/bin --application '--nice-name=com.smartisanos.security' com.android.internal.os.WrapperInit 35 28 'android.app.ActivityThread' '0'

这就说明我们设置好了

1.3不断复现你的问题并且抓取分配内存的调用栈:adb shell dumpsys meminfo --unreachable pid | tee app_size.txt

1.4对比你APP 不同native size产生的日志问题,寻找问题

例如:

com.smartisanos.security APP Native Heap:    66696byte 时有如下栈信息:

 Unreachable memory

  65376 bytes in 1349 unreachable allocations

  ABI: 'arm64'



  48 bytes unreachable at 75aa29e0b0

   and 62832 similar unreachable bytes in 1309 allocations

   referencing 1248 unreachable bytes in 26 allocations

   first 32 bytes of contents:

   75aa29e0b0: 18 67 43 4d 76 00 00 00 00 00 00 00 00 00 00 00 .gCMv...........

   75aa29e0c0: 00 00 00 00 00 00 00 00 b0 90 fa ad 75 00 00 00 ............u...

          #00  pc 000000000006a4f8  /system/lib64/libc++.so (operator new(unsigned long)+32)

          #01  pc 000000000007062c  /system/lib64/libhwui.so

          #02  pc 0000000000068818  /system/lib64/libhwui.so

          #03  pc 000000000006c404  /system/lib64/libhwui.so

          #04  pc 0000000000075a84  /system/lib64/libhwui.so (android::uirenderer::renderthread::RenderThread::threadLoop()+340)

          #05  pc 00000000000114e8  /system/lib64/libutils.so (android::Thread::_threadLoop(void*)+280)

          #06  pc 00000000000ad7ec  /system/lib64/libandroid_runtime.so (android::AndroidRuntime::javaThreadShell(void*)+140)

          #07  pc 000000000006827c  /system/lib64/libc.so

          #08  pc 000000000001f550  /system/lib64/libc.so

          #09  pc 0000000000000000  <unknown>

com.smartisanos.security APP Native Heap:    66696byte 时有如下栈信息:

Unreachable memory

  271152 bytes in 5638 unreachable allocations

  ABI: 'arm64'



  48 bytes unreachable at 75a70be430

   and 261408 similar unreachable bytes in 5446 allocations

   referencing 9168 unreachable bytes in 191 allocations

   first 32 bytes of contents:

   75a70be430: 18 67 43 4d 76 00 00 00 00 00 00 00 00 00 00 00 .gCMv...........

   75a70be440: 00 00 00 00 00 00 00 00 b0 90 fa ad 75 00 00 00 ............u...

          #00  pc 000000000006a4f8  /system/lib64/libc++.so (operator new(unsigned long)+32)

          #01  pc 000000000007062c  /system/lib64/libhwui.so

          #02  pc 0000000000068818  /system/lib64/libhwui.so

          #03  pc 000000000006c404  /system/lib64/libhwui.so

          #04  pc 0000000000075a84  /system/lib64/libhwui.so (android::uirenderer::renderthread::RenderThread::threadLoop()+340)

          #05  pc 00000000000114e8  /system/lib64/libutils.so (android::Thread::_threadLoop(void*)+280)

          #06  pc 00000000000ad7ec  /system/lib64/libandroid_runtime.so (android::AndroidRuntime::javaThreadShell(void*)+140)

          #07  pc 000000000006827c  /system/lib64/libc.so

          #08  pc 000000000001f550  /system/lib64/libc.so

          #09  pc 0000000000000000  <unknown>

我们发现这个栈是一样的,但是占用的内存越来越大,那么我们直接去这个栈里排查是哪里创建了内存,解析了so得到的行号如下:

Stack Trace:
  RELADDR           FUNCTION                                                                                                                                                                                       FILE:LINE
  000000000006a4f8  operator new(unsigned long)+32                                                                                                                                                                 external/libcxxabi/src/cxa_new_delete.cpp:46
  000000000007062c  android::uirenderer::renderthread::RenderMonitor::addFrame(android::uirenderer::FrameInfo&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)+456  frameworks/base/libs/hwui/renderthread/RenderMonitor.cpp:116
  0000000000068818  android::uirenderer::renderthread::CanvasContext::draw()+616                                                                                                                                   frameworks/base/libs/hwui/renderthread/CanvasContext.cpp:489
  000000000006c404  android::uirenderer::renderthread::DrawFrameTask::run()+324                                                                                                                                    frameworks/base/libs/hwui/renderthread/DrawFrameTask.cpp:114
  0000000000075a84  android::uirenderer::renderthread::RenderThread::threadLoop()+340                                                                                                                              frameworks/base/libs/hwui/renderthread/RenderThread.cpp:376
  00000000000114e8  android::Thread::_threadLoop(void*)+280                                                                                                                                                        system/core/libutils/include/utils/StrongPointer.h:?
  00000000000ad7ec  android::AndroidRuntime::javaThreadShell(void*)+140                                                                                                                                            frameworks/base/core/jni/AndroidRuntime.cpp:1256
  000000000006827c  __pthread_start(void*)+36                                                                                                                                                                      bionic/libc/bionic/pthread_create.cpp:226
  000000000001f550  __start_thread+68                                                                                                                                                                              bionic/libc/bionic/clone.cpp:47

我们可以发现这个内存是在RenderMonitor.cpp的116行申请的,那么我们看下对应的代码:

这里创建了一个MonitorTask对象,我们可以猜到这个对象估计是没有释放,那么我们找找它被释放的地方,我们发现这个对象每次执行完才会被delete:

我们继续找找如果task没有被执行呢?我们看到如下代码:

如果mMonitorTaskQueued为true,mAnimatorTaskQueued为true的时候,那么这个task就不会被delete了,到这里我们也就找到了泄漏的原因,因为这里不符合条件导致task永远不会被delete,那么我们做如下修改:

当不符合条件的时候delete task就好了

 

2.使用native_heapdump_viewer.py分析

    有时候我们用dumpsys meminfo --unreachable打印出来的调用栈并没有什么用,即使内存泄漏了,这里显示的内存大小还是差不多的,那么我们就得用native_heapdump_viewer来分析了,使用方法如下:

1.1复现问题多次dump native内存信息:

adb shell am dumpheap -n 6813 /data/local/tmp/heap_6813.txt   // 6813是你进程的pid

1.2解析内存信息:

native_heapdump_viewer.py --symbols /home/pzc/sd/log/giant/417189/6-10/symbols ./heap_6813_109444.txt > heap_109444.txt // symbols一定要对应你的rom

1.3对比解析出来的内存信息:

例如我的两次解析结果对比,com.smartisanos.security native size为:115588byte时的信息:

15241187  33.27%  33.27%     6038   7c73b6e550 /system/lib64/libc.so __start_thread bionic/libc/bionic/clone.cpp:47
 15241187  33.27% 100.00%     6038     7c73bb727c /system/lib64/libc.so __pthread_start(void*) bionic/libc/bionic/pthread_create.cpp:226
 15141747  33.05%  99.35%     4784       7c744fd7ec /system/lib64/libandroid_runtime.so android::AndroidRuntime::javaThreadShell(void*) frameworks/base/core/jni/AndroidRuntime.cpp:1256
 15141747  33.05% 100.00%     4784         7c732e04e8 /system/lib64/libutils.so android::Thread::_threadLoop(void*) system/core/libutils/include/utils/StrongPointer.h:?
 15107271  32.97%  99.77%     4672           7c761e7ab8 /system/lib64/libhwui.so android::uirenderer::renderthread::RenderThread::threadLoop() frameworks/base/libs/hwui/renderthread/RenderThread.cpp:376
 14977578  32.69%  99.14%     3981             7c761e6ca0 /system/lib64/libhwui.so android::uirenderer::renderthread::SignalingRenderTask::run() frameworks/base/libs/hwui/renderthread/RenderTask.cpp:27
 14977578  32.69% 100.00%     3981               7c761e692c /system/lib64/libhwui.so android::uirenderer::renderthread::MethodInvokeRenderTask::run() frameworks/base/libs/hwui/renderthread/RenderTask.h:85
  8978727  19.60%  59.95%     2262                 7c761e551c /system/lib64/libhwui.so android::uirenderer::renderthread::Bridge_dumpProfileInfo(android::uirenderer::renderthread::dumpProfileInfoArgs*) frameworks/base/libs/hwui/renderthread/RenderProxy.cpp:427
  8441856  18.43%  94.02%     2061                   7c76202b6c /system/lib64/libhwui.so android::uirenderer::FrameInfoVisualizer::dumpData(int) frameworks/base/libs/hwui/FrameInfoVisualizer.cpp:249
  8441856  18.43% 100.00%     2061                     7c73bb26cc /system/lib64/libc.so fwrite bionic/libc/upstream-openbsd/lib/libc/stdio/fwrite.c:83
  8441856  18.43% 100.00%     2061                       7c73bb2198 /system/lib64/libc.so __sfvwrite bionic/libc/upstream-openbsd/lib/libc/stdio/fvwrite.c:60
  8441856  18.43% 100.00%     2061                         7c73bb46e8 /system/lib64/libc.so __swsetup bionic/libc/upstream-openbsd/lib/libc/stdio/wsetup.c:73
  8441856  18.43% 100.00%     2061                           7c73bb2a64 /system/lib64/libc.so __smakebuf bionic/libc/upstream-openbsd/lib/libc/stdio/makebuf.c:62

com.smartisanos.security native size为:130088byte时的信息:

 28115314  48.67%  48.67%     9142   7c73b6e550 /system/lib64/libc.so __start_thread bionic/libc/bionic/clone.cpp:47
 28115314  48.67% 100.00%     9142     7c73bb727c /system/lib64/libc.so __pthread_start(void*) bionic/libc/bionic/pthread_create.cpp:226
 28012378  48.50%  99.63%     7870       7c744fd7ec /system/lib64/libandroid_runtime.so android::AndroidRuntime::javaThreadShell(void*) frameworks/base/core/jni/AndroidRuntime.cpp:1256
 28012378  48.50% 100.00%     7870         7c732e04e8 /system/lib64/libutils.so android::Thread::_threadLoop(void*) system/core/libutils/include/utils/StrongPointer.h:?
 27976768  48.43%  99.87%     7750           7c761e7ab8 /system/lib64/libhwui.so android::uirenderer::renderthread::RenderThread::threadLoop() frameworks/base/libs/hwui/renderthread/RenderThread.cpp:376
 27850347  48.21%  99.55%     7176             7c761e6ca0 /system/lib64/libhwui.so android::uirenderer::renderthread::SignalingRenderTask::run() frameworks/base/libs/hwui/renderthread/RenderTask.cpp:27
 27850347  48.21% 100.00%     7176               7c761e692c /system/lib64/libhwui.so android::uirenderer::renderthread::MethodInvokeRenderTask::run() frameworks/base/libs/hwui/renderthread/RenderTask.h:85
 16669534  28.86%  59.85%     4204                 7c761e551c /system/lib64/libhwui.so android::uirenderer::renderthread::Bridge_dumpProfileInfo(android::uirenderer::renderthread::dumpProfileInfoArgs*) frameworks/base/libs/hwui/renderthread/RenderProxy.cpp:427
 15638528  27.07%  93.82%     3818                   7c76202b6c /system/lib64/libhwui.so android::uirenderer::FrameInfoVisualizer::dumpData(int) frameworks/base/libs/hwui/FrameInfoVisualizer.cpp:249
 15638528  27.07% 100.00%     3818                     7c73bb26cc /system/lib64/libc.so fwrite bionic/libc/upstream-openbsd/lib/libc/stdio/fwrite.c:83
 15638528  27.07% 100.00%     3818                       7c73bb2198 /system/lib64/libc.so __sfvwrite bionic/libc/upstream-openbsd/lib/libc/stdio/fvwrite.c:60
 15638528  27.07% 100.00%     3818                         7c73bb46e8 /system/lib64/libc.so __swsetup bionic/libc/upstream-openbsd/lib/libc/stdio/wsetup.c:73
 15638528  27.07% 100.00%     3818                           7c73bb2a64 /system/lib64/libc.so __smakebuf bionic/libc/upstream-openbsd/lib/libc/stdio/makebuf.c:62

我们发现这个栈申请的内存越来越大,然后我们就去排查这个栈的代码,我们发现最好malloc是从

FrameInfoVisualizer.cpp:249这里开始的,这里申请了内存但是并没有释放,这里也没有fclose(file);

查了一下fprintf的资料,大概意思是:

​fprintf​:写入指定的流(这里会创建内存)

​dprintf​:写入指定的文件描述符(这个看上去并不会创建内存,而是直接写到fd里)

对比了下Q上的代码发现google也发现了这里有问题,并做了修改:

commit 5a44b4ff74fa7b27963c22249c815aef6225cb8d
Author: John Reck <jreck@google.com>
Date:   Mon Nov 13 11:32:39 2017 -0800

    Fix leak of FILE* in dumping
    
    Avoid fdopen as fclose, which frees the FILE*, will close
    the FD which we don't want. Just normalize on dprintf instead,
    and we can add buffering if it turns out to matter at some point
    
    Test: ran 'dumpsys gfxinfo framestats' in a loop while observing PSS
    Change-Id: I7808753641aa1055cfdf570c3e017017f11f1dee

感兴趣的同学可以再自行研究一下​fprintf​和​dprintf​的区别,这里就不继续介绍了

 

出现问题的原因:

    正常用户不会遇到这个问题,这个只有在dumpsys gfxinfo APP的时候会走到,我们性能监控的工具刚好每隔几秒钟会去dumpsys一次,然后就会一直泄漏,修复方案:

https://android.googlesource.com/platform/frameworks/base/+/47f5c3a234c5c201ef640489af3ff25b5eec6652

三.总结

    分析内存泄漏要是没有掌握好调试工具,犹如大海捞针,分析异常的困难,掌握了malloc_debug的使用方法,可以让大家分析native内存泄漏问题的效率也大大的提升~

  • 2
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
本文基于针对集合类对象的内存泄漏检测方案实现了口oudFoundry云平台 中Java应用内存泄漏的检测,通过监控集合类对象的内存消耗和集合内元素的 使用情况,得出对象内存泄漏的可能性大小,量化对象内存泄漏的风险。检测 系统首先收集垃圾回收事件后的应用内存数据,确定进行内存泄漏分析的时间 区间。然后通过收集每次垃圾回收事件后对象之间的引用关系和对象内存大小, 计算得到集合类对象的内存影响值。接着通过修改字节码的方式获取到集合类 对象使用集合内元素的数据,计算出相应的元素使用影响值。最后根据内存影 响值和元素使用影响值计算出集合对象的可信值,并提供对象的初始化位置信 息用于定位可疑对象。 本文主要使用JavaInstrument,ASM,JVMInterfaceTools等技术实现了内 存泄漏检测系统,该系统根据功能划分为JavaAgent、NativeAgent和可信值计 算H个模块,JavaAgent主要用于收集集合内元素使用数据,NativeAgent主要 用于收集内存使用数据。我们将该内存检测系统部署在CloudFoundry上,作为 云平台中应用性能管理工具的一部分,为云平台用户提供Java应用内存泄漏检 测功能,帮助开发者定位和解决内存泄漏问题。此外,本文还对该系统进行了 可用性测试和运行时系统负载测试。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值