Android内存泄漏分析

Android系统查找native进程的内存泄漏

问题描述

在系统运行过程中,使用android自带的procrank工具,查看系统内存情况,例如

130|shell@android:# procrank | head                       
warning: could not create process interface for 8695
  PID       Vss      Rss      Pss      Uss     Swap    PSwap  cmdline
 2524   353316K   145292K   141318K   139760K       0K       0K  /system/bin/mediaserver
 3866  1030380K   58352K   34293K   33188K    1096K      26K  com.autonavi.amapautolite
  797  1102276K   53088K   27254K   25688K    1080K      29K  system_server
 1247   981064K   41256K   18633K   17824K    1132K      27K  com.iflytek.inputmethod
 1099  1006660K   42044K   17814K   16576K    1116K      54K  com.android.systemui

明显可以看出mediaserver进程的内存偏高,存在内存泄漏的情况,亦或者可能是频繁的内存分配/释放导致的内存碎片问题,不过大部分情况都是内存泄漏。

问题查找

针对mediaserver的内存泄漏,Android已经提供了非常好的工具来分析。想必是开发过程中google也遇到这个进程不少的内存泄漏情况,也导致新版本的Android已经将这个进程拆分成多个子进程,好收敛问题。

言归正传。

Android针对内存泄漏的情况,有一个libc_malloc_debug.leak.so来替代libc,这个库可以通过编译bionic来得到。它会记录每次内存分配/释放的堆栈,这样我们周期性的打印这些堆栈,通过前后的对比,就可以看到内存的变化情况。

首先,要让link启动进程的时候加载这个库,而非libc.so,需要做以下设置:
setprop libc.debug.malloc 1
setprop libc.debug.malloc.program mediaserver
意思是只debug mediaserver的内存情况,而且只针对内存泄漏,其他设置内容可以参考代码。

开机以后,可以运行

dumpsys media.player -m

系统会调用

MediaPlayerService::dump => 
dumpMemoryAddresses(fd) => 
get_malloc_leak_info (libc_malloc_debug.leak.so)

可以打印如下的堆栈,其中记录了内存占用总量,以及每一块内存的分配堆栈
Android内存泄漏分析_第1张图片

等系统运行一段时间以后,再次dumpsys,就可以得到下图的比较,其中右侧红色的部分就有可能是内存泄漏的项目
Android内存泄漏分析_第2张图片

以上已经大致看到了可能的内存泄漏情况,那么如何查找到对应的代码呢?

问题修正

此时我们可以打印进程的maps文件,来得到系统库的内存分布,命令如下:
Android内存泄漏分析_第3张图片

如果我们得到一段泄漏的堆栈情况:

size       64, dup 62340, 0xb314b50e, 0xb6ed8376, 0xb69a3b1c, 0xb32ba0e4, 0xb32ba586, 0xb32a81f4, 0xb451b03e, 0xb451d1b4, 0xb688972a, 0xb688a56e, 0xb688953e, 0xb6899c74, 0xb6899dc2, 0xb6899e6c, 0xb699acc0, 0xb6edc642, 0xb6eda5f6

有点天书的样子,不过有进程的maps,又有这里的堆栈,实际上每个地址都可以在maps当中查到对应的库,然后减去库的首地址,就可以得到库当中的偏移地址,使用addr2line就可以得到实际对应的代码了。

使用以下脚本
参考脚本

可以得到如:

MAPFILE=maps.txt STACKFILE=stack.txt ROOTPATH=~/out/target/product/joyasz6580_weg_l/symbols
callstack = size  3133464, dup   12, 0xb30c650e, 0xb30c6914, 0xb6e5339e, 0xaee22478, 0xaee1c770, 0xb5e7bdda, 0xb69cb1ce, 0xb69cb370, 0xb69d3e04, 0xb6322334, 0xb69cc792, 0xb69d359c, 0xb632219c, 0xb69c523a, 0xb6322f4e, 0xb63228a4, 0xb6915cc0, 0xb691582e, 0xb6e57642, 0xb6e555f6
Ignore not address: size
Ignore not address: 3133464
Ignore not address: dup
Ignore not address: 12
findAddrInMap: targetAddr 0xb30c650e
path=~/out/target/product/joyasz6580_weg_l/symbols/system/lib/libc_malloc_debug_leak.so offsetHex=650e
leak_malloc
~/bionic/libc/bionic/malloc_debug_leak.cpp:315 (discriminator 1)


findAddrInMap: targetAddr 0xb30c6914
path=~/out/target/product/joyasz6580_weg_l/symbols/system/lib/libc_malloc_debug_leak.so offsetHex=6914
leak_memalign
~/bionic/libc/bionic/malloc_debug_leak.cpp:473


findAddrInMap: targetAddr 0xb6e5339e
path=~/out/target/product/joyasz6580_weg_l/symbols/system/lib/libc.so offsetHex=1239e
memalign
~/bionic/libc/bionic/malloc_debug_common.cpp:276


findAddrInMap: targetAddr 0xaee22478
path=~/out/target/product/joyasz6580_weg_l/symbols/system/lib/libMtkOmxVenc.so offsetHex=11478
addr2line: '~/out/target/product/joyasz6580_weg_l/symbols/system/lib/libMtkOmxVenc.so': No such file


findAddrInMap: targetAddr 0xaee1c770
path=~/out/target/product/joyasz6580_weg_l/symbols/system/lib/libMtkOmxVenc.so offsetHex=b770
addr2line: '~/out/target/product/joyasz6580_weg_l/symbols/system/lib/libMtkOmxVenc.so': No such file

findAddrInMap: targetAddr 0xb5e7bdda
path=~/out/target/product/joyasz6580_weg_l/symbols/system/lib/libstagefright_omx.so offsetHex=15dda
android::OMXNodeInstance::allocateBuffer(unsigned int, unsigned int, unsigned int*, void**)
~/frameworks/av/media/libstagefright/omx/OMXNodeInstance.cpp:880


findAddrInMap: targetAddr 0xb69cb1ce
path=~/out/target/product/joyasz6580_weg_l/symbols/system/lib/libstagefright.so offsetHex=a91ce
android::ACodec::allocateBuffersOnPort(unsigned int)
~/frameworks/av/media/libstagefright/ACodec.cpp:1158

以上就得到了需要确认的代码内存,白盒查一下是否真的有内存泄漏吧。

你可能感兴趣的:(Android)