Android内存优化(二)之如何分析native heap

如何获取native heap请阅读上篇文章,本篇文章将主要介绍如何分析一个native heap文件,以及我们的native memory leak问题如何发现并定位。

首先先来看一下实际中我们看到的native heap(我临时dump的com.android.settings进程的native heap):

Android Native Heap Dump v1.0

Total memory: 17898619
Allocation records: 32501
Backtrace size: 16

z 1 sz 585216 num 1 bt 00000074c95904ec 00000074b3a7fa94 00000074b3a29f38 00000074b3a38c84 00000074b3a389c8 00000074b3a38650 00000074b3a3339c 00000074c5b1bce8 00000074c89d3334 00000074c89d39f8 00000074c89d2090 00000074c89cee2c 00000074c89d6984 00000074c89d8678 00000074c89d95f0 00000074c5f6260c
z 1 sz 585216 num 1 bt 00000074c95904ec 00000074b3a7fa94 00000074b3a29f38 00000074b3a38c84 00000074b3a389c8 00000074b3a38650 00000074b3a3339c 00000074c5b1bce8 00000074c89d3334 00000074c89d39f8 00000074c89d2090 00000074c89cee2c 00000074c89d6984 00000074c89d8678 00000074c89d95f0 00000074c5f6260c
……….
MAPS
12c00000-52c00000 rw-p 00000000 00:05 134210 /dev/ashmem/dalvik-main space (region space) (deleted)
70c26000-70c44000 rw-p 00000000 103:05 3139681 /data/dalvik-cache/arm64/system@[email protected]
70c44000-70c46000 rw-p 00000000 103:05 3139683 /data/dalvik-cache/arm64/system@[email protected]
70c46000-70f2b000 rw-p 00000000 103:05 3139684 /data/dalvik-cache/arm64/system@[email protected]
70f2b000-71071000 rw-p 00000000 103:05 3139686 /data/dalvik-cache/arm64/system@[email protected]

我们平时dump出来的内存就是这种形式,主要包括了三类信息,第一类信息是此次dump的整体情况,主要涵盖Total memory,Allocation records,Backtrace size,第二类信息是具体的内存分配信息,形式如z 0 sz 400 num 1 bt 0000a230 0000b500,第三类信息是smaps内存映射信息,用来辅助native heap的分析。

native heap中的各种信息,具体是什么含义呢?有一段英文资料解释的比较清楚:

The data has this header:

Android Native Heap Dump v1.0

Total memory: XXXX
Allocation records: YYYY
Backtrace size: ZZZZ
Total memory is the total of all of the currently live allocations. Allocation records is the total number of allocation records. Backtrace size is the maximum number of backtrace frames that can be present.

Following this header are two different sections, the first section is the allocation records, the second section is the map data.

The allocation record data has this format:

z ZYGOTE_CHILD_ALLOC  sz    ALLOCATION_SIZE  num  NUM_ALLOCATIONS bt FRAMES
ZYGOTE_CHILD_ALLOC is either 0 or 1. 0 means this was allocated by the zygote process or in a process not spawned from the zygote. 1 means this was allocated by an application after it forked off from the zygote process.

ALLOCATION_SIZE is the size of the allocation. NUM_ALLOCATIONS is the number of allocations that have this size and have the same backtrace. FRAMES is a list of instruction pointers that represent the backtrace of the allocation.

Example:
z 0  sz      400  num    1  bt 0000a230 0000b500
z 1  sz      500  num    3  bt 0000b000 0000c000
The first allocation record was created by the zygote of size 400 only one with this backtrace/size and a backtrace of 0xa230, 0xb500. The second allocation record was create by an application spawned from the zygote of size 500, where there are three of these allocation with the same backtrace/size and a backtrace of 0xb000, 0xc000.

The final section is the map data for the process:

MAPS
7fe9181000-7fe91a2000 rw-p 00000000 00:00 0                              /system/lib/libc.so
.
.
.
END

那我们拿到这些信息如何分析呢?
AOSP中有个工具,可以直接分析,叫native_heapdump_viewer.py,下载连接:https://github.com/aosp-mirror/platform_development/blob/master/scripts/native_heapdump_viewer.py

Analyzing heap dumps

To analyze the data produced by the dumpheap command, run this script:

development/scripts/native_heapdump_viewer.py
In order for the script to properly symbolize the stacks in the file, make sure the script is executed from the tree that built the image.

To collect, transfer, and analyze a dump:

adb shell am dumpheap -n <PID_TO_DUMP> /data/local/tmp/heap.txt
adb shell pull /data/local/tmp/heap.txt .
python development/scripts/native_heapdump_viewer.py --symbols /some/path/to/symbols/ heap.txt > heap_info.txt
At the moment, the script will look for symbols in the given directory, using the path the .so file would have on the device. So if your .so file is at /data/app/.../lib/arm/libx.so on the device, it will need to be at /some/path/to/symbols/data/app/.../lib/arm/libx.so locally given the command line above. That is: you need to mirror the directory structure for the app in the symbols directory.

感兴趣的可以看下native_heapdump_viewer,py的源码,尤其是从frames里如何将地址还原为哪个方法,对应哪个源文件的哪一行的过程,还是蛮精彩的~大体逻辑是首先根据frames信息找到smaps中的映射,看到底是哪个二进制文件,再根据symbols库的信息,结合方法地址在二进制文件中的映射偏移量,计算出在二进制文件中的相对地址,在根据add2line工具找到方法名称以及对应的行数~
简单看一下输出结果:

This outputs a file with lines of the form:

BYTES %TOTAL %PARENT    COUNT    ADDR LIBRARY FUNCTION LOCATION
5831776  29.09% 100.00%    10532     71b07bc0b0 /system/lib64/libandroid_runtime.so Typeface_createFromArray frameworks/base/core/jni/android/graphics/Typeface.cpp:68

   5831776 is the total number of bytes allocated at this stack frame, which is 29.09% of the total number of bytes allocated and 100.00% of the parent frame's bytes allocated. 10532 is the total number of allocations at this stack frame. 71b07bc0b0 is the address of the stack frame.

一般通过这种方式可以发现是某个函数调用最终分配了多少内存,但对于内存泄漏貌似没什么帮助~那怎么分析内存泄漏问题呢?

关注内存泄漏问题,主要是关注测试开始与测试结束时内存增长情况,在分析java heap时,可以借助MAT提供的hprof文件的相减,再看引用关系。那么在分析native heap时,是否也可以有类似的分析方法呢?

代码目前还不能给出,感兴趣的可以交流。简单的思路是:在native_heapdump_viewer.py再封装一层,就像MAT分析hprof文件一样,将start和stop两个heap文件信息load进内存后,根据backtraces的frames信息对heap进行整理与解析,然后根据frames为判断依据,将相同的部分相减,得到内存增长的部分,然后再解析heap增长部分的调用栈信息,还原到文件的某一行,可以直接点看到由哪个函数引起的内存增长,增长了多少,以及具体的调用栈是啥。这个可以按大小输出,这样更直观的看到引起内存增长的最大问题。大概就是这样。后面的两篇博客就是基于这个工具而衍生出的一些探索和讨论,甚至说改进。

猜你喜欢

转载自blog.csdn.net/longlong2015/article/details/80540402