native内存泄漏分析
一.摘要
我们在分析内存泄漏时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 memory65376 bytes in 1349 unreachable allocationsABI: 'arm64'48 bytes unreachable at 75aa29e0b0and 62832 similar unreachable bytes in 1309 allocationsreferencing 1248 unreachable bytes in 26 allocationsfirst 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 memory271152 bytes in 5638 unreachable allocationsABI: 'arm64'48 bytes unreachable at 75a70be430and 261408 similar unreachable bytes in 5446 allocationsreferencing 9168 unreachable bytes in 191 allocationsfirst 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:LINE000000000006a4f8 operator new(unsigned long)+32 external/libcxxabi/src/cxa_new_delete.cpp:46000000000007062c 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:1160000000000068818 android::uirenderer::renderthread::CanvasContext::draw()+616 frameworks/base/libs/hwui/renderthread/CanvasContext.cpp:489000000000006c404 android::uirenderer::renderthread::DrawFrameTask::run()+324 frameworks/base/libs/hwui/renderthread/DrawFrameTask.cpp:1140000000000075a84 android::uirenderer::renderthread::RenderThread::threadLoop()+340 frameworks/base/libs/hwui/renderthread/RenderThread.cpp:37600000000000114e8 android::Thread::_threadLoop(void*)+280 system/core/libutils/include/utils/StrongPointer.h:?00000000000ad7ec android::AndroidRuntime::javaThreadShell(void*)+140 frameworks/base/core/jni/AndroidRuntime.cpp:1256000000000006827c __pthread_start(void*)+36 bionic/libc/bionic/pthread_create.cpp:226000000000001f550 __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:4715241187 33.27% 100.00% 6038 7c73bb727c /system/lib64/libc.so __pthread_start(void*) bionic/libc/bionic/pthread_create.cpp:22615141747 33.05% 99.35% 4784 7c744fd7ec /system/lib64/libandroid_runtime.so android::AndroidRuntime::javaThreadShell(void*) frameworks/base/core/jni/AndroidRuntime.cpp:125615141747 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:37614977578 32.69% 99.14% 3981 7c761e6ca0 /system/lib64/libhwui.so android::uirenderer::renderthread::SignalingRenderTask::run() frameworks/base/libs/hwui/renderthread/RenderTask.cpp:2714977578 32.69% 100.00% 3981 7c761e692c /system/lib64/libhwui.so android::uirenderer::renderthread::MethodInvokeRenderTask::run() frameworks/base/libs/hwui/renderthread/RenderTask.h:858978727 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:4278441856 18.43% 94.02% 2061 7c76202b6c /system/lib64/libhwui.so android::uirenderer::FrameInfoVisualizer::dumpData(int) frameworks/base/libs/hwui/FrameInfoVisualizer.cpp:2498441856 18.43% 100.00% 2061 7c73bb26cc /system/lib64/libc.so fwrite bionic/libc/upstream-openbsd/lib/libc/stdio/fwrite.c:838441856 18.43% 100.00% 2061 7c73bb2198 /system/lib64/libc.so __sfvwrite bionic/libc/upstream-openbsd/lib/libc/stdio/fvwrite.c:608441856 18.43% 100.00% 2061 7c73bb46e8 /system/lib64/libc.so __swsetup bionic/libc/upstream-openbsd/lib/libc/stdio/wsetup.c:738441856 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:4728115314 48.67% 100.00% 9142 7c73bb727c /system/lib64/libc.so __pthread_start(void*) bionic/libc/bionic/pthread_create.cpp:22628012378 48.50% 99.63% 7870 7c744fd7ec /system/lib64/libandroid_runtime.so android::AndroidRuntime::javaThreadShell(void*) frameworks/base/core/jni/AndroidRuntime.cpp:125628012378 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:37627850347 48.21% 99.55% 7176 7c761e6ca0 /system/lib64/libhwui.so android::uirenderer::renderthread::SignalingRenderTask::run() frameworks/base/libs/hwui/renderthread/RenderTask.cpp:2727850347 48.21% 100.00% 7176 7c761e692c /system/lib64/libhwui.so android::uirenderer::renderthread::MethodInvokeRenderTask::run() frameworks/base/libs/hwui/renderthread/RenderTask.h:8516669534 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:42715638528 27.07% 93.82% 3818 7c76202b6c /system/lib64/libhwui.so android::uirenderer::FrameInfoVisualizer::dumpData(int) frameworks/base/libs/hwui/FrameInfoVisualizer.cpp:24915638528 27.07% 100.00% 3818 7c73bb26cc /system/lib64/libc.so fwrite bionic/libc/upstream-openbsd/lib/libc/stdio/fwrite.c:8315638528 27.07% 100.00% 3818 7c73bb2198 /system/lib64/libc.so __sfvwrite bionic/libc/upstream-openbsd/lib/libc/stdio/fvwrite.c:6015638528 27.07% 100.00% 3818 7c73bb46e8 /system/lib64/libc.so __swsetup bionic/libc/upstream-openbsd/lib/libc/stdio/wsetup.c:7315638528 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 -0800Fix leak of FILE* in dumpingAvoid fdopen as fclose, which frees the FILE*, will closethe FD which we don't want. Just normalize on dprintf instead,and we can add buffering if it turns out to matter at some pointTest: ran 'dumpsys gfxinfo framestats' in a loop while observing PSSChange-Id: I7808753641aa1055cfdf570c3e017017f11f1dee
感兴趣的同学可以再自行研究一下fprintf和dprintf的区别,这里就不继续介绍了
出现问题的原因:
正常用户不会遇到这个问题,这个只有在dumpsys gfxinfo APP的时候会走到,我们性能监控的工具刚好每隔几秒钟会去dumpsys一次,然后就会一直泄漏,修复方案:
https://android.googlesource.com/platform/frameworks/base/+/47f5c3a234c5c201ef640489af3ff25b5eec6652
三.总结
分析内存泄漏要是没有掌握好调试工具,犹如大海捞针,分析异常的困难,掌握了malloc_debug的使用方法,可以让大家分析native内存泄漏问题的效率也大大的提升~
native内存泄漏分析相关推荐
- Android Native 内存泄漏系统化解决方案
导读:C++内存泄漏问题的分析.定位一直是Android平台上困扰开发人员的难题.因为地图渲染.导航等核心功能对性能要求很高,高德地图APP中存在大量的C++代码.解决这个问题对于产品质量尤为重要和关 ...
- Android 内存泄漏分析指北
android 内存泄漏分析指北 简单来说内存泄漏就是当对象不再被应用程序使用,但是垃圾回收器却不能移除它们,因为它们正在被引用 java 垃圾回收介绍: Java 虚拟机运行所管理的内存包括以下几个 ...
- Android Native内存泄漏诊断
Android Native内存泄漏诊断 1.基础诊断方法 特点:操作简单,但只能判断是否有泄漏,但需使用者自行判断泄漏在哪里 命令行方式 adb shell dumpsys meminfo vStu ...
- android释放acitity内存,Android 内存泄漏分析与解决方法
在分析Android内存泄漏之前,先了解一下JAVA的一些知识 1. JAVA中的对象的创建 使用new指令生成对象时,堆内存将会为此开辟一份空间存放该对象 垃圾回收器回收非存活的对象,并释放对应的内 ...
- 记一次 .NET 某外贸Web站 内存泄漏分析
一:背景 1. 讲故事 上周四有位朋友加wx咨询他的程序内存存在一定程度的泄漏,并且无法被GC回收,最终机器内存耗尽,很尴尬. 沟通下来,这位朋友能力还是很不错的,也已经做了初步的dump分析,发现了 ...
- 内存泄漏分析_调查内存泄漏第2部分–分析问题
内存泄漏分析 这个小型系列的第一个博客介绍了如何创建一个非常泄漏的示例应用程序,以便我们可以研究解决服务器应用程序上基于堆的问题的技术. 它展示了Producer-Consumer模式的一个大问题,即 ...
- 4大JVM性能分析工具详解,及内存泄漏分析方案
谈到性能优化分析一般会涉及到: Java代码层面的,典型的循环嵌套等 还会涉及到Java JVM:内存泄漏溢出等 MySQL数据库优化:分库分表.慢查询.长事务的优化等 阿里P8架构师谈:MySQL慢 ...
- Android内存泄漏分析及调试
2019独角兽企业重金招聘Python工程师标准>>> Android内存泄漏分析及调试 分类: Android2013-10-25 11:31 5290人阅读 评论(5) 收藏 举 ...
- Android常见的内存泄漏分析
内存泄漏原因 当应用不需要在使用某个对象时候,忘记释放为其分配的内存,导致该对象仍然保持被引用状态(当对象拥有强引用,GC无法回收),从而导致内存泄漏. 常见的内存泄漏源头 泄漏的源头有很多,有开源的 ...
- Android 内存泄漏分析与解决方法
Android 内存泄漏分析与解决方法 参考文章: (1)Android 内存泄漏分析与解决方法 (2)https://www.cnblogs.com/start1225/p/6903419.html ...
最新文章
- nginx 修复固定链接404
- 数据结构--环形链表
- mapper中的CDATA标签的用法
- linux下重启mysql php nginx
- page.ClientScript.RegisterStartupScript
- Oracle Partition By 的使用
- opencv中cvSetCaptureProperty定位不准的原因及解决(转载)
- 华为机试-字符串通配符
- Python+selenium自动化测试环境安装
- 机器学习指南_管理机器学习实验的快速指南
- 开启、关闭数据库mysql
- Jeesite进行打包(打成war包)
- java数组的实例化
- 调侃腾讯和360的小段子
- 互联网公司数据安全保护新探索
- 深圳软件测试培训:简述关系型数据库和非关系型数据库
- 前端WEB开发面试题整理
- H.266/VVC的编码结构和块划分
- 实验十二 团队项目用户验收评审
- QT应用编程: 编写MQTT客户端登录OnetNet服务器完成主题订阅与发布
热门文章
- 常见的文件格式有哪些
- matlab 多维svm分类代码,SVM多分类(matlab)
- gif android 点击 加载,Android两种简单的加载GIF图片的方法
- 微博html5到桌面,微博正文-微博HTML5版
- uniapp中使用colorUI说明文档
- Android 开源无线投屏,用树莓派Raspberry Pi 4B制作一个无线投屏器(20200803)
- win10+VS2013+OPENCV如何配置于仕琪人脸检测算法
- postsql时间计算
- 分治法--线性时间选择(求第k小数)
- oracle ebs教学视频教程,Oracle EBS教学视频