轉自:https://blog.csdn.net/jk198310/article/details/52065112
內存泄露問題在一些壓力測試的場景很容易暴露,例如一些常用應用場景反覆操作(eg:反覆切換前後攝像頭,反覆進入退出相機應用、壓力拍照等等)。
內存泄露一般表現爲:
①內存分配釋放,導致進程空間虛擬地址被分配完,或者物理地址被分配完。
②文件泄露,導致進程空間文件句柄數達到最大值。
③線程泄露,導致進程空間虛擬地址被分配完,進程內保留很多線程棧(stack)。
Android Native層中大部分的泄露問題都在HAL層,主要導致原因就是一些非對稱操作。
1. 工具
這一層主要使用工具爲Linux自帶的工具以及一些文件節點的狀態。
命令列表:
ps [-t] [pid] [ | busybox wc -l ] top [-t] ll /proc/pid/fd [ |busybox wc -l ] cat /proc/pid/maps [ |busybox wc -l ] cat /proc/meminfo [ | grep "MemFree" ] dumpsys meminfo [ pid ] [ package name ] procrank valgrind valgrind |
2. ps / top命令
ps /top 可以對linux系統中進程進行監測和控制。Ps 是顯示瞬間進程的狀態;Top是對進程運行時間監控。
Ps/top命令查找到目標的進程號pid,再根據pid去觀測反覆操作中Ps/top的兩項打印項:
VSIZE(VSS) :佔用的虛擬內存的大小。
RSS :佔用內存的大小。
確認下這兩項是否一直在無限制增大,這樣子可以初步確認一下內存泄露問題的存在性。
ps -t pid 這個命令可以列出當前進程所有線程,包括native線程和java線程。
native線程可以查看到其線程名,如:
USER PID PPID VSIZE RSS WCHAN PC NAME media 1574 1465 50312 9992 c0089920 b6ea491c SCameraCaptureTh |
Java 線程只能看到java線程在native層的映射名
USER PID PPID VSIZE RSS WCHAN PC NAME u0_a22 6884 2562 984204 54004 c0089920 4010491c SThread-194 |
查線程是否泄露技巧:可以在相同的狀態反覆使用下面命令,如在每次應用打開關閉後使用,這個命令可以統計目標進程內所有的線程數,這個數一直在增加的話,說明進程內存在進程內有線程沒有正常被釋放。
ps -t pid | busybox wc -c |
3. proc進程狀態節點
Linux proc虛擬文件系統中會記載系統所有進程的一些狀態信息,在/prco下會有進程目錄,目錄名就是pid。pid文件夾下信息量非常龐大,現在只介紹兩個與查內泄漏相關的:fd目錄和maps節點。
root@kylin-wt097:/ # ps mediaserver USER PID PPID VSIZE RSS WCHAN PC NAME media 8403 1 240688 16940 ffffffff b6f225a0 S /system/bin/mediaserver root@kylin-wt097:/ # ll /proc/8403
dr-xr-xr-x media audio 2012-02-14 12:55 attr ....................................... dr-x------ media audio 2012-02-14 12:55 fd -r--r--r-- media audio 0 2012-02-14 12:55 maps ....................................... -r--r--r-- media audio 0 2012-02-14 12:55 wchan |
①進程fd目錄
可以看出fd目下爲文件句柄的鏈接,例如17爲文件句柄號,/system/etc/camera.cfg爲打開的文件目錄。
root@kylin-wt097:/ # ll /proc/8403/fd
lrwx------ media audio 2012-02-14 13:02 0 -> /dev/null lrwx------ media audio 2012-02-14 13:02 1 -> /dev/null l-wx------ media audio 2012-02-14 13:02 10 -> /dev/log/system lr-x------ media audio 2012-02-14 13:02 11 -> /dev/__properties__ lr-x------ media audio 2012-02-14 13:02 16 -> /system/etc/camera.cfg lr-x------ media audio 2012-02-14 13:0217 -> /system/etc/camera.cfg l-wx------ media audio 2012-02-14 13:02 18 -> /dev/cpuctl/apps/tasks lrwx------ media audio 2012-02-14 13:02 3 -> /dev/binder lrwx------ media audio 2012-02-14 13:02 8 -> /dev/cpuctl/tasks l-wx------ media audio 2012-02-14 13:02 9 -> /dev/log/events ....................................... |
查看系統每個線程的文件句柄最大值,一般系統都會默認一個進程最多有1024個文件句柄,當一個進程打開文件句柄的數量達到1024時,再次創建文件句柄會失敗,strerror(errno)會報出“Too many open files”。
cat /proc/pid/limits | grep "Max open files" |
查文件句柄是否泄露技巧:可以在相同的狀態反覆使用下面命令,如在每次應用打開關閉後使用,這個命令可以統計目標進程內所有文件句柄,這個數一直在增加的話,說明進程內存在進程內有文件句柄沒有正常被關閉。
cat /proc/fd | busybox wc -c |
②進程maps節點
Maps節點可以查詢進程的虛內存空間的使用情況。
該文件有6列,分別爲:
地址:庫在進程裏地址範圍
權限:虛擬內存的權限,r=讀,w=寫,x=,s=共享,p=私有;
偏移量:庫在進程裏地址範圍
設備:映像文件的主設備號和次設備號;
節點:映像文件的節點號;
路徑: 映像文件的路徑
root@kylin-wt097:/ # cat /proc/8403/maps
a9035000-a9525000 rw-s 98bb3000 00:09 2159 anon_inode:dmabuf ................................................................ a95bf000-a96bc000 rw-p 00000000 00:00 0 [stack:11334] a99fc000-ab17b000 rw-s 96354000 00:0c 31508 /dev/video0 ab17b000-ac8fa000 rw-s 94bd5000 00:0c 31508 /dev/video0 af7f8000-b0f77000 rw-s 90558000 00:0c 31508 /dev/video0 b0f77000-b26f6000 rw-s 8edd9000 00:0c 31508 /dev/video0 ................................................................
b26f9000-b27f6000 rw-p 00000000 00:00 0 [stack:11307] b27f6000-b27f8000 rw-p 00000000 00:00 0 ................................................................ b4bb4000-b4bb5000 r--p 00001000 b3:07 1091 /system/lib/libril_audio.so b4bb5000-b4bb6000 rw-p 00002000 b3:07 1091 /system/lib/libril_audio.so .................................................................. b6f54000-b6f55000 r--p 0000f000 b3:07 149 /system/bin/linker b6f55000-b6f56000 rw-p 00010000 b3:07 149 /system/bin/linker b6f56000-b6f57000 rw-p 00000000 00:00 0 b6f57000-b6f59000 r-xp 00000000 b3:07 162 /system/bin/mediaserver b6f5a000-b6f5b000 r--p 00002000 b3:07 162 /system/bin/mediaserver b6f5b000-b6f5c000 rw-p 00000000 00:00 0 b852b000-b859d000 rw-p 00000000 00:00 0 [heap] bea7c000-bea9d000 rw-p 00000000 00:00 0 [stack] ffff0000-ffff1000 r-xp 00000000 00:00 0 [vectors]
|
32位Liunx系統每個進程有4G地址空間,android系統下進程地址空間分部如下:
高位1G空間爲內核地址空間,地位3G空間爲用戶地址空間,可以看出棧stack是有高位向低位增長,而堆heap是由地位向高位增長,還有一段加載動態庫的段,可以根據上面的catmaps打印出來對比。
堆泄露的定位方法:
下面是調用mmap映射得到進程虛擬地址,這樣的打印持續增多的話,說明進程中有mmap 和 munmap沒有對稱操作,導致進程虛擬地址的泄露。
ab17b000-ac8fa000 rw-s 94bd5000 00:0c 31508 /dev/video0 |
下面是一個線程棧的使用情況, [stack:11334],11334爲線程號tid,這樣的打印持續增多的話,說明進程中會持續創建線程,但是沒有釋放舊線程。
a95bf000-a96bc000 rw-p 00000000 00:00 0 [stack:11334] |
③meminfo節點
proc/meminfo節點記錄了系統內存的一些使用情況,主要看MemFree這項。
root@kylin-p1:/ # cat /proc/meminfo cat /proc/meminfo MemTotal: 1673008 kB所有可用RAM大小(即物理內存減去一些預留位和內核的二進制代碼大小) MemFree: 761320 kB LowFree與HighFree的總和,被系統留着未使用的內存 .......................................... Mapped: 87932 kB 設備和文件等映射的大小。 .......................................... |
4. Android 工具
這是Android上實現的一些命令,在Native這層也可以使用,但是統計的一些信息也和上面的一樣。
①dumpsys meminfo [ pid ] [ package name]
可以查看到某個線程(包括應用應用和系統線程)內存使用情況,包括Native堆和java堆。一般用來查java應用的進程,對於系統Native進程,dump出來信息較少。
②procrank
Androidprocrank (/system/xbin/procrank) 工具,能夠列出進程所佔用的內存使用情況。順序爲從高到低。每個進程佔用內存大小以 VSS, RSS , PSS, USS 的形式列出。爲了簡化描述,內存佔用以頁爲單位表述,而不是字節。 通常每頁爲 4096 字節。(和ps功能差不多,數據有少許差異)。
③valgrind
android sdk默認集成了valgrind,一款優秀的內存問題檢測工具,能夠發現內存泄漏。
當前的方案默認沒有編譯該工具,可以通過以下命令臨時編譯:
$ cd external/valgrind $ mm -j16 |
然後回到android根目錄編譯生產system.img。對於系統啓動運行的進程需要,如debug surfaceflinger,需要在init.rc註釋surfaceflinger service,改爲命令行啓動,
$root@kylin-perf:/ # valgrind --leak-check=full --log-file=/data/valgrind.log /system/bin/surfaceflinger & |
進行界面的一些簡單操作後將surfacelinger進程kill掉,valgrind即會將分析報告輸出到/data/valgrind.log,裏面信息很多,附上相關的範例log:
關注leak summary:
==1982== LEAK SUMMARY: ==1982== definitely lost: 58,004 bytes in 477 blocks ==1982== indirectly lost: 376 bytes in 8 blocks ==1982== possibly lost: 122,997 bytes in 484 blocks ==1982== still reachable: 922,973 bytes in 20,141 blocks ==1982== suppressed: 0 bytes in 0 blocks ==1982== Reachable blocks (those to which a pointer was found) are not shown. ==1982== To see them, rerun with: --leak-check=full --show-reachable=yes ==1982== ==1982== For counts of detected and suppressed errors, rerun with: -v ==1982== Use --track-origins=yes to see where uninitialised values come from ==1982== ERROR SUMMARY: 673822 errors from 827 contexts (suppressed: 0 from 0)
|
轉自: http://blog.csdn.net/wlwl0071986/article/details/49700789