Android memory leak detect

来源:互联网 发布:华为云计算招聘 编辑:程序博客网 时间:2024/06/06 00:44

  • What is memory leak
  • Native process memory leak detect
  • Java process memory leak detect
  • Kmemleak
    • usage
    • applied
    • Basic Algorithm
  • Other memory leak detector

What is memory leak

内存泄漏指程序未能释放已经不再使用的内存。内存泄漏并非指内存在物理上的消失,而是应用程序分配内存后,由于设计错误,导致在释放该段内存之前就失去了对该段内存的控制,从而造成了内存的浪费。

当进程使用的内存随着时间的推移,越来越多,而且通常是只增不减,那么我们可以判断这个进程出现了内存泄露。

Android系统上,内存泄露有三种情况:
1. Java 进程内存泄露(java heap上的泄露)
2. Native进程或 java native heap 上泄露(JNI代码)
3. Kernel 内存泄露

这三种情况,要用不同的方法来查。

Native process memory leak detect

目前android上,最好用的方法是android 官网上推荐的:
https://android.googlesource.com/platform/bionic/+/master/libc/malloc_debug/README.md

Android 7.0以后,使用以下方法:

setprop libc.debug.malloc.options backtracesetprop libc.debug.malloc 1setprop libc.debug.malloc.program /system/bin/app_process

当指定这个prop :libc.debug.malloc.program时,leak detect只对这个进程有效。上面的例子中,是对所有的java进程进行检测。如果没有指定这个prop,则对所有的进程进程检测。

由于所有的native程序申请内存时,使用的是android的libc库。所以track native memory需要使用另一版的libc:libc_malloc_debug_leak.so libc_malloc_debug_qemu.so。这两个so只在userdebug和eng版本有。其中libc_malloc_debug_qemu.so是给emulate用的。所以要debug的话,需要编个userdebug 或 eng的版本。libc.debug.malloc 这个prop的含义如下:

1  - perform leak detection5  - fill allocated memory to detect overruns10 - fill memory and add sentinels to detect overruns, also check memory leak20 - use special instrumented malloc/free routines for the emulator

Libc.debug.malloc level设成10也是可以检测泄露的,但是只有在进程退出的时候才会打印没有free的内存信息。使用起来没有level 1方便。

以mediaserver为例。在设置libc.debug.malloc后,要kill mediaserver.(killall -HUP mediaserver),让mediaserver申请内存时使用debug libc。具体步骤如下:

  1. dump mediaserver’s memory use dumpsys command
    dumpsys media.player -m > /data/before.txt
  2. Reproduce mediaserver memroy leak problem and dump again
    dumpsys media.player -m > /data/after.txt
  3. diff the difference
    diff before.txt after.txt > diff.txt
  4. Get the maps file of mediaserver
    cat /proc/[pidofmediaserver]/maps > /data/maps
  5. Analysis and map back the backtrace to function symbols and line number

7.0之前的版本需要做第4步,7.0之后,第4步libc默认会做的。7.0之前的版本,可以使用下面的help工具。
https://github.com/tinypie/android-memleak-help

7.0之前的版本,dumpsys media.player -m 输出结果很多条,比如下面这条:

size 1590888, dup 2, 0xb4a9a1bc, 0xb4a9a630, 0xb6eb7dc0, 0xb5077980, 0xb4eff7c4, 0xb4f0e684, 0xb4de63e0, 0xb4f1710c, 0xb4f9fbe4, 0xb4f989a8, 0xb4c2b668, 0xb4b495b8, 0xb4b4adc0, 0xb4b4880c, 0xb4b09a38, 0xb6eb71a0, 0xb6eb7338

含义如下:

  • 分配的大小,单位 bytes
  • 重复的个数
  • backtrace address(up to 32)

这些backtracd地址不结合maps文件看,是没有意义的。以上面为例,假设maps如下:

b4a8d000-b4ace000 r-xp 00000000 b3:09 41059    /system/lib/libc_malloc_debug_leak.sob4ace000-b4ad0000 r--p 00040000 b3:09 41059    /system/lib/libc_malloc_debug_leak.sob4ad0000-b4ad3000 rw-p 00042000 b3:09 41059    /system/lib/libc_malloc_debug_leak.so

需要将backtrace地址减去map的基地址,算出偏移量。比如b4a9a1bc 在b4a8d000-b4ace000这个范围内,偏移量

b4a9a1bc - b4a8d000 = d1bc

然后,用addr2line 找到代码行:

addr2line -f -e symbols/system/lib/libc_malloc_debug_leak.so d1bc/mnt/source/android/bionic/libc/bionic/malloc_debug_leak.cpp:282

Mediaserver 有dumpsys 接口,可以从libc里dump信息。如果是其它的native出现了内存泄漏,怎么办?这时就需要添加代码,仿照mediaserver里的dump接口。

source file:frameworks/av/media/libmediaplayerservice/MediaPlayerService.cppbionic/libc/bionic/malloc_debug_common.cpp

Java process memory leak detect

Android java 虚拟机(无论是dalvik还是art)会自己管理java heap,而JNI代码则和native 进程一样使用libc 管理heap。所以java heap 和native heap 出现泄露时,detect方法也不一样。

Java进程 java heap 的内存泄漏就好查的多,因为有ddms这个工具可以使用。如果是Java 进程的native heap出现了内存泄漏,怎么办?

Ddms也可以查看java进程的native heap,方法如下:
Platform setup(userdebug or eng)

setprop libc.debug.malloc.options backtracesetprop libc.debug.malloc 1setprop libc.debug.malloc.program /system/bin/app_process

computer setup
1. 打开 ~/.android/ddms.cfg
2. 在这个文件的结尾添加
native=true
3. 保存文件
4. 从android sdk里单独打开ddms,确保不跑Eclispse
5. 打开Ddms,连接adb
6. Select process and track the allocation

如果没有ddms,可以使用am dumpheap命令。am dumpheap [pid or process name],输出的文件是hprof文件,需要用MAT进行分析。如果用-n选项dump native heap的话,map backtrace的方法,上面已经介绍过了。

am dumpheap: dump the heap of a process.  The given <PROCESS> argument may  be either a process name or pid.  Options are:    -n: dump native heap instead of managed heap    --user <USER_ID> | current: When supplying a process name,        specify user of process to dump; uses current user if not specified.

Kmemleak

见内核文档 Documentation/kmemleak.txt

usage

打开以下kconfig

CONFIG_DEBUG_KMEMLEAK=yCONFIG_DEBUG_KMEMLEAK_EARLY_LOG_SIZE=10000

1、如果没有挂载debugfs
mount -t debugfs nodev /sys/kernel/debug/
2、开启内核检测线程
echo scan > /sys/kernel/debug/kmemleak
3、查看leak 信息
cat /sys/kernel/debug/kmemleak
4、清除内核检测报告,新的内存泄露报告将重新写入
echo clear > /sys/kernel/debug/kmemleak

内存扫描参数可以进行修改,方法是向/sys/kernel/debug/kmemleak 节点写入参数。 参数如下:

off 禁用kmemleak(不可逆)stack=on 启用任务堆栈扫描(default)stack=off 禁用任务堆栈扫描scan=on 启动自动记忆扫描线程(default)scan=off 停止自动记忆扫描线程scan=<secs> 设置n秒内自动记忆扫描,默认600sscan 开启内核扫描clear 清除内存泄露报告dump=<addr> 转存信息对象在<addr>

applied

kmemleak可以检测以下类型的memory leak

  • kmalloc/kzalloc
  • vmalloc
  • kmem_cache_alloc
  • Per_cpu
  • [Page allocations and ioremap are not tracked]

Basic Algorithm

  • 将所有的分配对象放到白名单里(白名单里剩余的对象稍后被认为是孤儿对象(orphan)
  • 扫描kernel data/bss 段,然后扫描所有的page和进程的stack,将匹配的地址存在PRIO搜索树。如果一个白名单对象的指针被发现,将该对象添加到灰名单中
  • 扫描与灰名单对象匹配的地址(一些白名单对象可以放到灰名单里),直到灰名单结束
  • 剩下的白名单对象被认为是孤儿,写入/sys/kernel/debug/kmemleak。

Other memory leak detector

Native 层的memory leak 检测还可以使用
Valgrind
Address sanitizer