内存优化整理
- 一、dumpsys meminfo 查詢內存劃分
- 二、Java內存優化 - 2.1 java內存泄漏優化
 
- 三、Native內存優化 - 3.1 malloc_debug模式
- 3.1?使用 python 分析
 
- 四、線程/fd泄漏分析 - 4.1 fd簡述
- 4.2 常見原因
- 4.3 查看fd
- 4.4 查看線程樹
- 4.5 查看線程調用棧
 
- 五、附(Python3環境工具)
一、dumpsys meminfo 查詢內存劃分
使用dumpsys meminfo??[packageName] 命令可以查詢該進程下pss(Proportional Set Size?實際使用的物理內存)內存劃分
重點字段解讀:
-  Native Heap - Native 堆內存:調用?malloc/new?(C/C++)占據的內存 
-  Dalvik Heap - Dalvik虛擬機內存,Dalvik虛擬機代碼在?libdvm.so?主要負責運行時dex解析成機器碼 
-  .art mmap - Android RunTime 映射內存,art 代碼在?android_runtime.so, mmap(是linux C 的一個函數接口,用來做內存映射) 
-  Private Dirty - 進程獨占的內存,內存已經被本進程修改過,只能被自己進程使用 
-  Private clean - 進程獨占的內存,內存是映射過來的,沒有做修改,可以置換給到其他進程使用 
-  java heap - java堆內存 =?Dalvik heap(dirty+ clean) + art heap (dirty + clean) 
- Total - 整個應用占據的總內存
-  Graphics - 用于圖形緩沖區隊列的內存,用于在屏幕上顯示像素,包括GL表面,GL紋理等(如果沒有用到 OpenGL 或者不是游戲,可以直接忽略) 
-  Stack - 線程棧占據的內存 
-  Code -?應用程序使用代碼和資源的內存,如dex字節碼,優化或編譯的dex代碼,.so庫和字體? 
二、Java內存優化
2.1 java內存泄漏優化
java內存泄漏的主要是由于java對象被單例、匿名內部類、監聽類等持有而無法釋放導致的,具體的排查和解決方法可以參考:
內存泄漏優化整理
三、Native內存優化
android中Native內存泄漏的原因較為復雜,可能是添加ImageView過多或者開啟硬件加速導致Bitmap像素內存增長,或是某個播放庫申請內存較多,故需要多個方面去定位分析。
3.1 malloc_debug模式
malloc_debug是谷歌推薦的一種定位Native內存泄露方法,需要root權限?。以下為各個android版本開啟malloc_debug的方式:
1)如果是Android 8.x,執行以下命令(N為調用棧層級數,推薦為2)
| 1 2 3 | //查詢所有內存 adb shell setprop wrap.<APP_PACKAGE_NAME>?'"LIBC_DEBUG_MALLOC_OPTIONS=backtrace=N? logwrapper"' adb shell am force-stop <APP_PACKAGE_NAME> | 
2)如果是Android 7.x (8.x 也可以用),執行以下命令,其中app_process照寫,不需要針對調試的app更改
| 1 2 3 4 5 | adb root adb shell stop adb shell setprop lib.debug.malloc.program app_process adb shell setprop lib.debug.malloc.options?"\"backtrace leak_track\"" adb shell start | 
3)如果是Android 5.x / 6.x,執行以下命令
| 1 2 3 4 | adb root adb shell setprop libc.debug.malloc=1 adb shell stop adb shell start | 
執行命令后,在應用操作復現路徑或者monkey一段時間后,使用以下命令dump出native內存
| 1 | adb shell am dumpheap -n <APP_PACKAGE_NAME> /data/local/tmp/native.txt | 
3.1?使用 python 分析
使用開源的Python工具native_heapdump_viewer.py分析native內存
1)環境配置:
python2運行環境
arm-linux-androideabi-objdump環境變量配置
arm-linux-androideabi-addr2line環境變量配置
存放帶有符號信息so文件的文件夾(/path/to/symbols/)
2)運行命令:
| 1 | python native_heapdump_viewer.py --html --symbols? /path/to/symbols/??native.txt > heap_info.html | 
3)malloc_debug 內存文件分析:
查看生成的heap_info.html
字段解讀:
BYTES- 占用的內存大小單位byte
 %TOTAL - 占總 native 內存百分比
 %PARENT - 占父幀內存百分比
 COUNT - 調用了多少次
 ADDR- 內存地址
 LIBRARY - 占用的內存所屬哪一個 so 庫
 FUNCTION- 占用的內存所屬哪一個方法
 LOCATION - 占用的內存所屬哪一行
可以看出Native總內存約329M,其中libhwui.so庫申請bitmap像素內存100M,占比30%;libHCDNClientNet.so庫申請內存67M,占比20%;libgnustl_shared.so庫申請內存43M,占比13%。
此處申請bitmap像素內存和播放庫內存占比較高,需要再結合實際情況跟進分析。
四、線程/fd泄漏分析
4.1 fd簡述
Fd的全稱是File descriptor,在Linux OS里,所有都可以抽象成文件,比如普通的文件、目錄、塊設備、字符設備、socket、管道等等。當通過一些系統調用(如open/socket等),會返回一個fd(一個數字),然后根據這個fd對應的文件進行操作,比如讀、寫。在內核進程結構體task_struct中為每個進程維護了一個數組,數組下標就是fd,里面存儲的是對這個文件的描述。
Linux默認每個進程能打開的fd數最大值是1024,當應用進程打開的文件數超過這個限制值后就無法再打開文件了,系統會報相關的錯誤,Too many open files是Linux系統中程序打開的文件數過多的常見錯誤。
4.2 常見原因
1)HandlerThread
 若頻繁地創建HandlerThread對象,內部的Looper創建會導致fd超限。
 2)Thread.start()
 調用Thread.start()啟動線程后,在native層調用open方法創建ashmem rigions,這會產生fd,如果循環地創建啟動過多的線程就會產生fd泄露。
 3)Resource相關
 使用輸入輸出流沒有關閉可能會出問題,FileInputStream、FileOutputStream、FileReader、FileWriter 等每打開一個文件需要fd,一些輸入流也提供了基于fd的構造方法。文件流在finally塊中調close方法,或者使用Java 7 編譯器和運行環境支持的 try-with-resources 語句可以避免fd泄露。
 4)InputChannel
 對于system server,,如果有大量的socket 打開,可能是因為Input Channel沒有關閉,抓取hprof查看system server中WindowState的情況進行分析。
 5)Bitmap
 創建Bitmap在native層會產生fd,如果使用完后不關閉,可能引發fd的泄露。
4.3 查看fd
| 1 2 3 4 | //查看每個fd ls -a -l /proc/<PID>/fd //查看fd總數 ls -a -l /proc/<PID>/fd | wc -l | 
4.4 查看線程樹
| 1 | pstree <PID> | 
4.5 查看線程調用棧
| 1 2 3 4 | //java線程可以用DDMS工具或者jstack查看調用棧 jstack [-l] <PID> //native線程可以用pstack工具(依賴gdb)查看調用棧 sh pstack.sh <TID> | 
正在上傳…重新上傳取消?gdb正在上傳…重新上傳取消?pstack.sh
五、附(Python3環境工具)
正在上傳…重新上傳取消?native_heapdump_viewer.py
總結
 
                            
                        - 上一篇: 三部曲简史mobi_尤瓦尔.赫拉利简史三
- 下一篇: 教你Zbrush 4R7如何创建Z球
