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 實現的。
-
當一個 Activity 需要被回收時,就將其包裝到一個 WeakReference 中,並且在 WeakReference 的構造器中傳入自定義的 ReferenceQueue。
-
然後給包裝後的 WeakReference 做一個標記 Key,並且在一個強引用 Set 中添加相應的 Key 記錄
-
最後主動觸發 GC,遍歷自定義 ReferenceQueue 中所有的記錄,並根據獲取的 Reference 對象將 Set 中的記錄也刪除
-
經過上面 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
-
在其watch()方法中,主要操作
- 生成一個隨機的字符串 key,key 就是用來標識 WeakReference 的,就相當於給 WeakReference 打了個標籤
- 將被檢測對象包裝到一個 WeakReference 中,並將其標識爲步驟 1 中生成 key
- 調用 ensureGoneAsync 開始執行檢測操作
-
ensureGoneAsync 方法操作,其中實現了內存泄漏的檢測
-
遍歷 ReferenceQueue 中所有的元素,並根據每個元素中的 key,相應的將集合 retainedKeys 中的元素也刪除
-
判斷集合 retainedKeys 是否還包含被檢測對象的弱引用,包含說明被檢測對象並沒有被回收,發生了內存泄漏
-
生成 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;
}
});