開發中,如果代碼不規範很容易產生內存泄漏,比如Handler、Context、線程等使用。本文使用Android Studio自帶的Profiler和MAT工具進行內存泄漏分析。
一、內存泄漏
1、定義handler
private Handler mHandler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(@NonNull Message msg) {
mHandler.sendEmptyMessage(0);
return false;
}
});
2、在onCreate()方法調用
mHandler.sendEmptyMessage(0);
以上件簡單大的幾句代碼就發生了內存泄漏,可以用profiler工具查看。
二、profiler
1、使用profiler運行程序
點擊小圖標即可
2、選擇分析Memory
點擊MEMORY一欄
3、按返回鍵退出當前頁面,然後點擊垃圾桶小圖標
4、在回收後截取內存的一塊
5、底部按包名查找,找到對應包名下的文件
Allocations:申請的大小
Deallocations:釋放的大小
Total Count:對象引用次數
Shallow Size:對象自身佔用的內存大小
這裏重點關注第三個參數,我們已經退出了當前的MainActivity界面,但是發下對象的引用個數爲1,說明它沒有被回收。發生了內存泄漏。下面使用MAT工具,分析具體的內存泄漏原因。
三、MAT
1、點擊如下圖標:
2、將選中的內存保存下來,後綴爲hprof
3、只分析內存部分,需要使用命令行對文件進行過濾
找到hprof-conv.exe所在的目錄,一般在SDK目錄下的platform-tools目錄。截圖如下:
把文件複製到這裏。然後執行命令行,生成新的文件memory-2.hprof。命令行如下:
hprof-conv -z 當前文件 目標文件
4、用MAT工具打開當前文件
(1)點擊Histogram。
(2)在這裏可以輸入你要分析的文件名稱,由上面的Profile工具分析可以看出泄漏的文件爲MainActivity。所以直接輸入MainActivity,按回車,右擊當前文件
排除掉所有的弱引用和軟引用等。我們只分析強引用對象。
(3)展開引用,排除系統的引用。查看哪些外部對象引用它
(4)如下圖
排除系統的,發現MainActiivty$2內部類仍然連接着原始對象this。說明this是一個根對象,Handler是與他相連的。導致它無法回收。
四、解決
(1)方式一:使用移除消息的方式
在onDestroy()方法中添加如下代碼:
mHandler.removeMessages(0);
優點:能夠解決內存泄漏。
缺點:如果我們需要在界面結束的時候,需要繼續在後臺完成一個操作。完成後需要Handler通知,那麼這種方式就失效了。
(2)方式二:使用弱引用
代碼如下:
private class MyHandler extends Handler {
private WeakReference<MainActivity> mWeakReference;
MyHandler(MainActivity activity) {
mWeakReference = new WeakReference<>(activity);
}
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
Log.e("11111111111", "2222222222222");
if (mWeakReference.get() != null) {
mHandler.sendEmptyMessage(0);
}
}
}
private Handler mHandler = new MyHandler(this);
優點:能夠解決內存泄漏。
缺點:由於採用弱引用,不清楚什麼時候被回收。代碼不可控。
(3)方式三:使用靜態內部類
代碼如下:
private static class MyHandler extends Handler{
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
Log.e("11111111111","2222222222222");
mHandler.sendEmptyMessage(0);
}
}
private static Handler mHandler = new MyHandler();
增加了static修飾符。靜態內部類不持有外部類的引用。
優點:能夠解決內存泄漏。
缺點:由於static修飾的對象,生命週期比較長。所以大量使用static容易導致內存溢出。
綜上,還是要看自己的具體業務邏輯,採用不同的方式。