android內存泄漏記錄

android內存泄漏點記錄

常見的四種情況

1. 將context或者view置爲static(view會默認持有一個context的引用,置爲static的話會造成view在方法區中無法快速被回收,從而導致activity的泄漏)

public class TestActivity extends AppCompatActivity {

    private static ImageView imageView;

    @Override protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_test);
        imageView = findViewById(R.id.test_img);
        imageView.setImageResource(R.drawable.ic_launcher_background);
    }
}

上述的代碼中的imageView會導致TestActivity無法被GC回收

2. 常見的未解註冊的各種listener(如廣播,Observer的監聽等)

3. 非靜態Handler導致泄漏

    //------------------------註釋1-------------------------------
    private Handler handler = new Handler() {
        @Override
        public void handleMessage(@NonNull Message msg) {
            super.handleMessage(msg);
        }
    };

    //------------------------註釋2-------------------------------
    private static class MyHandler extends Handler {
        private final WeakReference<TestActivity> mActivity;
        public MyHandler(TestActivity activity) {
            mActivity = new WeakReference<>(activity);
        }
        @Override
        public void handleMessage(@NonNull Message msg) {
            TestActivity testActivity = mActivity.get();
            if (testActivity != null) {
                // do something
            }
        }
    }

上述代碼中註釋1是日常開發中寫的樣子也會造成Activity的內存泄漏,一般需要將其置爲static,然後內部持有一個Activity的弱引用來避免內存泄漏。如註釋2所示。

4.三方庫使用的Context

三方庫初始化的時候一般使用applicationContext作爲上下文參數,一般如果我們自己開發相關的SDK的時候一般用context.getApplicationContext() 的方法避免內存泄漏

LeakCanary的使用及原理分析

原理:

​ LeakCanary 中對內存泄漏檢測的核心原理就是基於 WeakReference 和 ReferenceQueue 實現的。

  1. 當一個 Activity 需要被回收時,就將其包裝到一個 WeakReference 中,並且在 WeakReference 的構造器中傳入自定義的 ReferenceQueue。

  2. 然後給包裝後的 WeakReference 做一個標記 Key,並且在一個強引用 Set 中添加相應的 Key 記錄

  3. 最後主動觸發 GC,遍歷自定義 ReferenceQueue 中所有的記錄,並根據獲取的 Reference 對象將 Set 中的記錄也刪除

  4. 經過上面 3 步之後,還保留在 Set 中的就是:應當被 GC 回收,但是實際還保留在內存中的對象,也就是發生泄漏了的對象。

主要觸發

LeakCanary 中監聽 Activity 生命週期是由 ActivityRefWatch 來負責的,主要是通過註冊 Android 系統提供的 ActivityLifecycleCallbacks,來監聽 Activity 的生命週期方法的調用

 new Application.ActivityLifecycleCallbacks() {
            @Override
            public void onActivityCreated(@NonNull Activity activity, @Nullable Bundle savedInstanceState) {
            }
            @Override
            public void onActivityStarted(@NonNull Activity activity) {
            }
            @Override
            public void onActivityResumed(@NonNull Activity activity) {
            }
            @Override
            public void onActivityPaused(@NonNull Activity activity) {
            }
            @Override
            public void onActivityStopped(@NonNull Activity activity) {
            }
            @Override
            public void onActivitySaveInstanceState(@NonNull Activity activity, @NonNull Bundle outState) {
            }
            @Override
            public void onActivityDestroyed(@NonNull Activity activity) {
            }
        };

當監聽到 Activity 的 onDestroy 方法後,會將其傳給 RefWatcher 的 watch 方法

核心類 RefWatcher

  1. 在其watch()方法中,主要操作

    1. 生成一個隨機的字符串 key,key 就是用來標識 WeakReference 的,就相當於給 WeakReference 打了個標籤
    2. 將被檢測對象包裝到一個 WeakReference 中,並將其標識爲步驟 1 中生成 key
    3. 調用 ensureGoneAsync 開始執行檢測操作
  2. ensureGoneAsync 方法操作,其中實現了內存泄漏的檢測

    1. 遍歷 ReferenceQueue 中所有的元素,並根據每個元素中的 key,相應的將集合 retainedKeys 中的元素也刪除

    2. 判斷集合 retainedKeys 是否還包含被檢測對象的弱引用,包含說明被檢測對象並沒有被回收,發生了內存泄漏

    3. 生成 Heap “堆”信息,並生成內存泄漏的分析報告

檢測時機 爲了儘量不影響 UI 線程的渲染,LeakCanary 也做了些優化操作

向主線程 MessageQueue 中插入了一個 IdleHandler,IdleHandler 只會在主線程空閒時纔會被 Looper 從隊列中取出並執行。通過 addIdleHandler 也經常用來做 App 的啓動優化,比如在 Application 的 onCreate 方法中經常做 3 方庫的初始化工作。可以將優先級較低、暫時使用不到的 3 方庫的初始化操作放到 IdleHandler 中,從而加快 Application 的啓動過程

//getMainLooper().myQueue()或者Looper.myQueue()
Looper.myQueue().addIdleHandler(new IdleHandler() {  
    @Override  
    public boolean queueIdle() {  
        //你要處理的事情
        return false;    
    }  
});
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章