Android在bionic libc中实现了一种方法用于定位堆内存(heap)泄露的位置以及内存越界(此处我们只关注内存泄露问题)
开启libc debug
在Android系统中,通过属性来控制libc debug开关。不同的Android版本,所使用的属性会有所不同:
Android6.0以及更早的版本
目标进程从zygote派生
1
2
3setprop libc.debug.malloc 1
stop
start目标进程不是从zygote派生
1
2setprop lib.malloc.debug 1
kill -9 [目标进程ID]
Android7.0及以后的版本
目标进程从zygote派生
1
2
3
4setprop libc.debug.malloc.program [目标进程名称]
setprop libc.debug.malloc.options backtrace
stop
start目标进程不是从zygote派生
1
2
3setprop libc.debug.malloc.program [目标进程名称]
setprop libc.debug.malloc.options backtrace
kill -9 [目标进程ID]用signal控制libc debug
1
2
3
4setprop libc.debug.malloc.options backtrace_enable_on_signal
stop
start
kill -45 <目标进程>与
backtrace
这种options不同的地方,就是backtrace_enable_on_signal
可以允许你在合适的时机再开启目标进程的libc debug功能。
获取heap信息
对不同类型的进程,在打开了该进程的libc debug开关之后,就可以使用对应的方法来获得该进程的heap信息
派生自Zygote的进程
派生自Zygote的进程,可以通过am dumpheap
命令获得heap内存信息
1 | am dumpheap -n <PID> [/save/path] |
所得到的heap信息如下
1 | Android Native Heap Dump v1.2 |
注意:Android 7/8 版本的heap信息中,并不会合并同类项(即backtrace相同的项),使得heap信息看起来很长;而Android9虽然有合并同类项,但解析脚本又按照旧的方式解析(未合并同类项的方式),所得到的解析后的数据是有问题的。这些问题在HeapSnap里面都有解决。
mediaserver进程
1 | dumpsys media.player -m |
其它进程
除了派生自Zygote的进程,以及mediaserver进程之外,其它的进程Android不支持获取heap信息,但我们可以通过第三方的工具实现,比如:HeapSnap
1 | /data/local/tmp/heapsnap -p <pid> -l /data/local/tmp/libheapsnap.so |
heap信息被保存在/sdcard/heap_snap/
,其内容如下
1 | Heap Snapshot v1.0 |
解析Heap信息
使用am dumpheap
命令所得到的heap信息中,backtrace是未经解析的地址,需要使用Android SDK工程中的脚本进行解析;用heapsnap工具得到的heap信息,已经有做了部分解析,如果想要更详细的解析,也可以使用Android SDK工程中的脚本对它进行解析。
注意:Android SDK工程代码需要跟所测试设备一致,否则很多地址会解析异常
把前面所保存的heap信息文件复制到android sdk中
使用脚本解析heap文件
1
development/scripts/native_heapdump_viewer.py heap.txt > heap_info.txt
也可以保存成html文件:
1
development/scripts/native_heapdump_viewer.py --html heap.txt > heap_info.html
android6.0及更早版本:
1
development/scripts/stack heap.txt > heap_info.txt
解析出来的信息如下:
1 | BYTES %TOTAL %PARENT COUNT ADDR LIBRARY FUNCTION LOCATION |