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增長部分的調用棧信息,還原到文件的某一行,可以直接點看到由哪個函數引起的內存增長,增長了多少,以及具體的調用棧是啥。這個可以按大小輸出,這樣更直觀的看到引起內存增長的最大問題。大概就是這樣。後面的兩篇博客就是基於這個工具而衍生出的一些探索和討論,甚至說改進。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章