1. 程式人生 > >效能優化之記憶體優化

效能優化之記憶體優化

效能優化之記憶體優化


計算 APP 獲得的最大記憶體分配值

Runtime rt=Runtime.getRuntime();
long maxMemory=rt.maxMemory();
Log.i("maxMemory:",Long.toString(maxMemory/(1024*1024)));

記憶體洩漏

一、記憶體洩漏檢視

1 生成.prof檔案
AS 內自帶的 Android Profilter 外掛
在相應的操作之後,點選 dump java heap 即進行一次GC 進行一次GC後 檢視記憶體情況 (若該釋放記憶體的地方沒有釋放 則說明存在記憶體洩漏)
生成.prof檔案


點選 左下角的 (Heap Dump左邊)的 一個匯出箭頭 然後儲存為 .hprof檔案

但是 此時的 .hprof 並非 MAT支援的型別 要轉型別


2 轉換.prof檔案
通過Android-Studio\platform-tools -> hprof-conv.exe 工具轉換
在hprof-conv.exe的目錄 右鍵命令列 把要轉換的原檔案放在同一個資料夾中
然後 用命令

hprof-conv leakfile.hprof newLeakFile.hprof 

即可在本目錄生成轉換格式的newLeakFile.hprof
然後就可以用MAT打開了


3 使用MAT(MemoryAnalyzer Tool)記憶體洩漏分析器 進行記憶體洩漏分析
MAT 中 開啟 Histogram(直方圖)
可以列出每個類的例項數
可以支援正則表達查詢 如找Activity 找某些類與物件

shallow heap 該物件所在的大小 不包括其引用的物件記憶體
Retained Heap 是 該物件及所引用的物件的總大小 即釋放後該物件GC可回收的大小
右鍵 list objects->:
with incoming references
表示的是 當前檢視的物件,被外部應用
with outGoing references


表示的是 當前物件,引用了外部物件
Merge shortest path to gc root (查詢gc root)
選擇exclude all phanthom weak soft(排除所有 虛 弱 軟引用)
如 Activity A 引用了 Activity B
可以在 histogram中 找ActivityB 是否存在 若存在則找被強引用的地方
Merge shortest path to gc root -> exclude all phanthom weak soft 中找到 Activity A引用了


常見記憶體洩漏情況:
(1) 物件被引用 未被回收 如Activity Context
解決: 找到引用該物件的例項,進行取消引用

(2) 單例模式引起的記憶體洩漏 單例模式生命週期與Application一致 因此 若將 某個Activity的引用作為單例模式的Context時 Activity被關閉了 但是 單例物件還是引用了該Activity 導致Activity未被釋放,而Activity中的如 Bitmap等物件也不能被釋放
解決: 將 context.getApplicationContext()傳給 單例 作Context

(3)在Acitivity中 寫了個非靜態內部類 並且 建立了他的靜態物件
解決:將內部類封裝成單例 context用ApplicationContext

(4)activity中 使用了執行緒 而執行緒未執行完
解決:Activity退出時要remove

(5) 屬性動畫 未 onDestroy或 onStop等時候 進行停止 導致Activity被View引用 無法釋放
解決: 及時的停止屬性動畫

(6) 訪問資料庫遊標未關閉
解決: 訪問完資料庫關閉遊標

(7) 註冊的動態廣播或者其他監聽未及時解註冊
解決: 及時解註冊


記憶體溢位
也是可以用上面的MAT進行記憶體分析

常見的記憶體溢位優化點:

1 bitmap過大 佔據太多記憶體

查詢圖片:
檢視到 domain Tree (支配樹)
然後 看到 android.graphics.Bitmap
點選 左側看到 圖片的 mWidth 和 mHeight 可以知道圖片的大小
選中“android.graphics.Bitmap”下方的 “byte[6553600]” 欄,
右鍵 - Copy - Save Value To File,將bitmap檔案儲存成字尾名為data的檔案
然後用 GIMP工具 開啟data字尾的圖片
設定寬高 ARGB open即可

優化措施:
1圖片太大 而具體的顯示區域比較小 導致浪費 因此可以對圖片進行縮放載入
BitmapFactory.Options引數,將這個引數的inJustDecodeBounds屬性設定為true就可以讓解析方法禁止為bitmap分配記憶體,
返回值也不再是一個Bitmap物件,而是null。雖然Bitmap是null了,
但是BitmapFactory.Options的outWidth、outHeight和outMimeType屬性都會被賦值。
這個技巧讓我們可以在載入圖片之前就獲取到圖片的長寬值和MIME型別,從而根據情況對圖片進行壓縮
2 介面被其他物件引用 無法及時釋放

3 不要使用setImageBitmap或setImageResource或BitmapFactory.decodeResource來設定一張大圖,
因為這些函式在完成decode後,最終都是通過java層的createBitmap來完成的,需要消耗更多記憶體
因此,改用先通過BitmapFactory.decodeStream方法,創建出一個bitmap,再將其設為ImageView的 source,decodeStream最大的祕密在於其直接呼叫JNI>>nativeDecodeAsset()來完成decode,
無需再使用java層的createBitmap,從而節省了java層的空間

4 及時回收bitmap
雖然GC會回收 但是不保證一定及時

drawble.recycle()
drawble.setCallback(null)

圖片壓縮處理參考:
https://blog.csdn.net/weixin_37577039/article/details/79777864


常見錯誤:

1 Error opening heap dump Unknown HPROF Version
解決: 上面第二步要轉型別