LeakCanary:檢測Android中的記憶體洩漏
阿新 • • 發佈:2019-02-20
Square開源了一個記憶體洩露自動探測神器——LeakCanary,它是一個Android和Java的記憶體洩露檢測庫,可以大幅度減少了開發中遇到的OOM問題,對於開發者來說,無疑是個福音,下面對該庫的使用進行簡單的介紹。
一 什麼是記憶體洩漏:
有些物件只有有限的生命週期。當它們的任務完成之後,它們將被垃圾回收。如果在物件的生命週期本該結束的時候,這個物件還被一系列的引用,這就會導致記憶體洩漏。隨著洩漏的累積,app將消耗完記憶體。
比如,在Activity.onDestroy()被呼叫之後,view樹以及相關的bitmap都應該被垃圾回收。如果一個正在執行的後臺執行緒繼續持有這個Activity的引用,那麼相關的記憶體將不會被回收,這最終將導致OutOfMemoryError崩潰。
二 LeakCanary介紹:
一個用於檢測Android&Java的記憶體洩漏檢測庫
A memory leak detection library for Android and Java.
“A small leak will sink a great ship.” - Benjamin Franklin
小漏不補沉大船。——本傑明 富蘭克林
三 DEMO使用:
(1)在build.gradle中新增依賴:
dependencies {
debugCompile 'com.squareup.leakcanary:leakcanary-android:1.4-beta2'
releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.4-beta2'
testCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.4-beta2'
}
(2)在Application中初始化Leak:
public class MyApp extends Application {
private static MyApp instance;
@Override
public void onCreate () {
super.onCreate();
instance = this;
LeakCanary.install(this);//初始化LeakCanary
}
public static final MyApp getInstance() {
return instance;
}
@Override
public void onTerminate() {
super.onTerminate();
}
}
或者這樣初始化:
public class MyApplication extends Application {
//在自己的Application中新增如下程式碼
public static RefWatcher getRefWatcher(Context context) {
ExampleApplication application = (MyApplication) context.getApplicationContext();
return application.refWatcher;
}
private RefWatcher refWatcher;
@Override public void onCreate() {
super.onCreate();
refWatcher = LeakCanary.install(this);
}
}
(3)在Activity中製造一個記憶體洩漏的場景:
MainActivity.java:
public class MainActivity extends AppCompatActivity {
public static MainActivity instance = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
instance = this;//建立一個靜態物件instance,保持其對當前activity的引用;
findViewById(R.id.bt).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent = new Intent();
intent.setClass(MainActivity.this, SecondActivity.class);
MainActivity.this.startActivity(intent);
}
});
}
@Override
protected void onDestroy() {
super.onDestroy();
//instance=null; //當要銷燬activity的時候,instance並未置空,仍然保持對MainActivity的引用,因此MainActivity不會銷燬;
}
}
SecondActivity.java:
public class SecondActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
findViewById(R.id.bt).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
MainActivity.instance.finish();//通過instance關閉MainActivity;
}
});
}
}
在finish的時候會出現記憶體洩漏,instance仍然保持對activity的引用。
(4)執行結果如下:
然後點選桌面的Leaks圖示可以看到:
Leaks指出了記憶體洩漏的位置是instance。
(5)LeakCanary的檢測工作機制:
LeakCanary檢測記憶體洩漏的原理大概分為7個步驟,從建立被關聯物件的弱引用、檢查該應用是否被清除等。
1、建立:RefWatcher.watch() 建立一個 KeyedWeakReference 到要被監控的物件。
2、檢查:然後在後臺執行緒檢查引用是否被清除,如果沒有,呼叫GC。
3、存放:如果引用還是未被清除,把 heap 記憶體 dump 到 APP 對應的檔案系統中的一個 .hprof 檔案中。
4、解析:在另外一個程序中的 HeapAnalyzerService 有一個 HeapAnalyzer 使用HAHA 解析這個檔案。
5、定位:得益於唯一的 reference key, HeapAnalyzer 找到 KeyedWeakReference,定位記憶體洩露。
6、計算:HeapAnalyzer 計算 到 GC roots 的最短強引用路徑,並確定是否是洩露。如果是的話,建立導致洩露的引用鏈。
7、展示:引用鏈傳遞到 APP 程序中的 DisplayLeakService, 並以通知的形式展示出來。
圖形路程圖如下所示:
參考致謝: