1. 程式人生 > >AndroidStudio + MAT 記憶體洩漏分析

AndroidStudio + MAT 記憶體洩漏分析

一概要:

記憶體洩漏是一些已經不使用的物件,依然佔有記憶體且垃圾回收機制不法回收它們。最終導致常駐記憶體越來越大

,影響到程式的效能。

在Android 虛擬機器中,採用Mark-Sweep方式實現垃圾回收。Mark標記,Sweep檢測。虛擬機器會從GC Roots開始

遍歷,如果某個節點無法找到一條到達GC Roots的路徑,則表示該引用無效,可以被回收。記憶體洩漏就是存在一些

不好的呼叫,導致一些無效的引用於GC Roots相連,無法被回收。

知道了原因,我們理應在開發中儘量避免這些。但是萬一發生了(程式碼又不是一個人寫的),我們要準確找出它們。

所以我們就要用到檢測記憶體洩漏的工具。

二使用:

例項程式碼(我們進入主頁面後,點選啟動TestSecondActivity,然後按返回退出):

   Intent intent2 = new Intent(MainActivity.this, TestSecondActivity.class);
   startActivity(intent2);
public class TestSecondActivity extends AppCompatActivity{

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_testsecond);
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                LogUtil.i("TestSE", "thread:" + Thread.currentThread() + "start");
                try{
                    Thread.sleep(80000l);
                }catch (Exception e){

                }
                LogUtil.i("TestSE", "thread:" + Thread.currentThread() + " end");
            }
        };
        new Thread(runnable).start();
    }
}

我們會通過兩種方式,來檢測記憶體洩漏:

#使用MAT用具查詢

1,首先開啟Android Studio中的Android Device Monitor如下圖。


2,開啟後會出現下面介面,選中你要檢測應用的包名,然後點選下圖畫圈的地方(Update Heap),會在程式包

名後標記一個圖示


3,接下就是我們的操作,,重複啟動回退TestSecondActivity 兩次,然後再點下圖畫圈地方(Dump HPROF file)

生成hprof檔案。


生成的hprof檔案。


4,因為MAT是用於分析java hprof的工具,我們生成的是Android屬性的hprof檔案,所以我們要轉化一下(我將剛剛

生成的檔案重名android.hprof)。

Android Sdk中為我們提供了工具:hprof-conv.exe。


轉化成了java屬性的檔案java.hprof。


開啟後如圖:


6,之後我們檢視記憶體中的物件,由於我們的記憶體洩漏一般都發生在Activity中所以我們之查詢Activity即可。

點選下圖中的QQL圖示,輸入select * from instanceof android.app.Activity,類似於SQL語句。紅色感嘆號為執行按鈕。

#記憶體中有兩個TestSecondActivity,但是我們已經退出了TestSecondActivity,說明發生了記憶體洩漏。

#上面有兩個屬性。Shallow Size物件自身佔用大小,不包括引用。Retained Size物件自身大小+ 直接或間接引用物件

的大小。

7,右擊TestSecondActivity,—— Path to GC Roots——All Reference。


看到this$0引用了這個Activity,this$0表示內部類意思。Activity被一個內部類引用,而這個內部類又被target(Thread)引用。

從上面的程式碼可知:

TestSecondActivity中有內部類runnable,而runnable被Thread使用。工作執行緒Thread需要sleep 80秒。所以導致

TestSecondActivity無法釋放。

解決方法。我們在Activity onDestroy中shutdown 執行緒Thread就可以了(當然先把Thread變成全域性變數)

#直接使用Android Studio上的Monitor Memory查詢

1,依然是利用上面的程式碼,執行程式,開啟Android Studio的Monitor介面,檢視Memory影象。


2,點選下卡車圖示(圖中1),可以執行一次GC;點選圖中2的位置,可以檢視hprof檔案。(依次點選1,2)

3,檢視結果


#同樣監聽到了TestSecondActivity的記憶體洩漏。

三注意

Android中記憶體引起記憶體洩漏的常見使用。

1,static變數引起的記憶體洩漏。

因為static生命週期是類載入時開始,類解除安裝結束。也就是說static變數在程式死亡時才結束。而如果static變數引用

Activity,那麼Activity的資源將一直得不到釋放。造成記憶體洩漏。

解決方法,如果需要Context物件,儘量使用getApplicationContext。假如必須使用Activity那麼最好在onDestroy方

法中,將Activity引用設定為null。

2,執行緒造成的記憶體洩漏。

類似於上面的例子,執行緒執行時間長,即使Activity結束了任然在執行。因為new Thread生成一個匿名內部類(內部

類的建立必須依靠外部類),因此握有Activity例項,Activity無法釋放。AsyncTask更為嚴重,因為AsyncTask維持一

個生命週期。

解決方法:合理安排執行緒,控制執行緒在Activity結束時結束。

3,Bitmap佔用過多的記憶體。

Bitmap解析需要佔用記憶體,但是Android只提供了8M記憶體給Bitmap,如果Bitmap過多且不能及時的回收。則會造成內

存溢位。

解決方法:及時recycle,載入圖片前先適當的壓縮圖片。

4,資源未被及時關閉造成記憶體洩漏

例如Cursor,沒有及時關閉,會因持有Activity引用,造成記憶體洩漏。

解決方法:在onDestroy中及時的關閉。

5,Handler的使用

Handler物件會發送Message到MessageQueue,Looper物件會輪詢MessageQueue物件去除Message執行。如果

Message物件長時間未被取出執行。則Message物件持有Handler物件,Handler物件持有Activity(可能)。造成洩漏。

解決方法,在onDestroy中,Handler removeMessage。

歡迎指正!