Leakcanary是由Square公司開源的一款輕量的第三方檢測內存泄露的工具java
主要原理 watch一個即將要銷燬的對象,好比監控一個activity處於什麼狀態。android
先來看一下java內存中幾個比較重要的部分git
棧(stack) 存放基本類型的數據和對象的引用,但對象自己不存放在棧中,而是存放在堆中github
堆(heap) 主要存放用new產生的數據,是垃圾回收器主要回收的部分app
方法區 存儲每一個類的信息(包括類的名稱、方法信息、字段信息)靜態變量、常量以及編譯器變異後的的代碼等dom
爲何會產生內存泄露異步
當一個對象已經再也不使用了,本應該回收,可是一個能到達GCRoot的對象還持有它的引用,致使它沒法被回收,還停留在堆內存中,致使內存泄漏ide
LeakCanary原理:函數
java中的4中引用類型工具
ReferenceQueue 引用隊列 軟引用和弱引用均可以和它集合使用,若是軟引用或者弱引用中的對象被垃圾回收了,java虛擬機會吧這個引用加入到與之關聯的引用隊列當中。
通常是在Application的onCreate方法中初始化
@Override
public void onCreate() {
super.onCreate();
if (LeakCanary.isInAnalyzerProcess(this)) {
return;
}
LeakCanary.install(this);
}
複製代碼
進入install方法
public static @NonNull RefWatcher install(@NonNull Application application) {
return refWatcher(application).listenerServiceClass(DisplayLeakService.class)
.excludedRefs(AndroidExcludedRefs.createAppDefaults().build())
.buildAndInstall();
}
複製代碼
返回一個RefWatcher對象,這個對象是用來監視應該成爲弱引用的對象。最終經過buildAndInstall()這個方法建立出來。
public @NonNull RefWatcher buildAndInstall() {
if (LeakCanaryInternals.installedRefWatcher != null) {
throw new UnsupportedOperationException("buildAndInstall() should only be called once.");
}
RefWatcher refWatcher = build();
//若是是在別的進程中,會跟DISABLED相等
if (refWatcher != DISABLED) {
if (enableDisplayLeakActivity) {
LeakCanaryInternals.setEnabledAsync(context, DisplayLeakActivity.class, true);
}
//默認爲true
if (watchActivities) {
ActivityRefWatcher.install(context, refWatcher);
}
//默認爲true
if (watchFragments) {
FragmentRefWatcher.Helper.install(context, refWatcher);
}
}
LeakCanaryInternals.installedRefWatcher = refWatcher;
return refWatcher;
}
複製代碼
經過build方法建立出RefWatcher,若是是別的進程,就直接返回成員變量DISABLED,若是不是建立新的RefWatcher並返回。
若是容許顯示內存泄露的Activity,就設置可顯示,DisplayLeakActivity就是當有內存泄露的時候,LeakCanary給咱們提供的可視化的那個界面
分別建立ActivityRefWatcher和FragmentRefWatcher,首先看ActivityRefWatcher.install方法
public static void install(@NonNull Context context, @NonNull RefWatcher refWatcher) {
Application application = (Application) context.getApplicationContext();
ActivityRefWatcher activityRefWatcher = new ActivityRefWatcher(application, refWatcher);
application.registerActivityLifecycleCallbacks(activityRefWatcher.lifecycleCallbacks);
}
private final Application.ActivityLifecycleCallbacks lifecycleCallbacks =
new ActivityLifecycleCallbacksAdapter() {
@Override public void onActivityDestroyed(Activity activity) {
refWatcher.watch(activity);
}
};
複製代碼
經過傳入的context拿到Application ,並建立ActivityRefWatcher ,最後經過application註冊Activity的生命週期回到函數,並傳入本身的callback。ActivityLifecycleCallbacksAdapter繼承自Android系統的Application.ActivityLifecycleCallbacks接口,主要爲了簡化代碼,由於只用到了onActivityDestroyed這一個方法。這個接口是Andorid系統爲咱們提供的能夠監聽到每一個Activity的生命週期。
在回調函數中能夠看到,當監聽到一個Activity銷燬的時候,就經過refWatcher.watch(activity)方法把這個Activity關聯到RefWatcher中。
在查看watch方法以前,先來看一下RefWatcher有哪些成員變量
public final class RefWatcher {
public static final RefWatcher DISABLED = new RefWatcherBuilder<>().build();
private final WatchExecutor watchExecutor;
private final DebuggerControl debuggerControl;
private final GcTrigger gcTrigger;
private final HeapDumper heapDumper;
private final HeapDump.Listener heapdumpListener;
private final HeapDump.Builder heapDumpBuilder;
private final Set<String> retainedKeys;
private final ReferenceQueue<Object> queue;
....
複製代碼
OK,如今去查看watch方法
public void watch(Object watchedReference) {
watch(watchedReference, "");
}
public void watch(Object watchedReference, String referenceName) {
if (this == DISABLED) {
return;
}
checkNotNull(watchedReference, "watchedReference");
checkNotNull(referenceName, "referenceName");
final long watchStartNanoTime = System.nanoTime();
String key = UUID.randomUUID().toString();
retainedKeys.add(key);
final KeyedWeakReference reference =
new KeyedWeakReference(watchedReference, key, referenceName, queue);
ensureGoneAsync(watchStartNanoTime, reference);
}
final class KeyedWeakReference extends WeakReference<Object> {}
複製代碼
建立了一個惟一key,而後放到成員變量set中保存,以後建立了一個KeyedWeakReference這個弱引用。用來保存須要分析的對象,最後執行異步方法ensureGoneAsync來分析這個弱引用對象。
private void ensureGoneAsync(final long watchStartNanoTime, final KeyedWeakReference reference) {
watchExecutor.execute(new Retryable() {
@Override public Retryable.Result run() {
return ensureGone(reference, watchStartNanoTime);
}
});
}
複製代碼
在子線程中執行ensureGone方法來分析對象是否真的被回收了
Retryable.Result ensureGone(final KeyedWeakReference reference, final long watchStartNanoTime) {
long gcStartNanoTime = System.nanoTime();
//從咱們調用watch方法到如今的總共使用的時間
long watchDurationMs = NANOSECONDS.toMillis(gcStartNanoTime - watchStartNanoTime);
//清除set集合中 已經到達引用隊列中的弱引用
removeWeaklyReachableReferences();
//若是在調試狀態 就不須要分析
if (debuggerControl.isDebuggerAttached()) {
// The debugger can create false leaks.
return RETRY;
}
//改對象沒有形成內存泄露
if (gone(reference)) {
return DONE;
}
//手動調用GC
gcTrigger.runGc();
//再次 清除set集合中 已經到達引用隊列中的弱引用
removeWeaklyReachableReferences();
//若是此時引用集合set中還包含改對象,那麼它就是個內存泄露的對象
if (!gone(reference)) {
long startDumpHeap = System.nanoTime();
long gcDurationMs = NANOSECONDS.toMillis(startDumpHeap - gcStartNanoTime);
//demp出一個 .hprof文件
File heapDumpFile = heapDumper.dumpHeap();
if (heapDumpFile == RETRY_LATER) {
// Could not dump the heap.
return RETRY;
}
long heapDumpDurationMs = NANOSECONDS.toMillis(System.nanoTime() - startDumpHeap);
HeapDump heapDump = heapDumpBuilder.heapDumpFile(heapDumpFile).referenceKey(reference.key)
.referenceName(reference.name)
.watchDurationMs(watchDurationMs)
.gcDurationMs(gcDurationMs)
.heapDumpDurationMs(heapDumpDurationMs)
.build();
//分析內存泄露
heapdumpListener.analyze(heapDump);
}
return DONE;
}
複製代碼
總結一下前面的代碼:
public interface Listener {
Listener NONE = new Listener() {
@Override public void analyze(HeapDump heapDump) {
}
};
void analyze(HeapDump heapDump);
}
@Override public void analyze(@NonNull HeapDump heapDump) {
checkNotNull(heapDump, "heapDump");
HeapAnalyzerService.runAnalysis(context, heapDump, listenerServiceClass);
}
複製代碼
analyze是Listener接口中的一個方法,它的實現類是在ServiceHeapDumpListener中。最後調用了HeapAnalyzerService.runAnalysis方法。
public static void runAnalysis(Context context, HeapDump heapDump, Class<? extends AbstractAnalysisResultService> listenerServiceClass) {
setEnabledBlocking(context, HeapAnalyzerService.class, true);
setEnabledBlocking(context, listenerServiceClass, true);
Intent intent = new Intent(context, HeapAnalyzerService.class);
intent.putExtra(LISTENER_CLASS_EXTRA, listenerServiceClass.getName());
intent.putExtra(HEAPDUMP_EXTRA, heapDump);
ContextCompat.startForegroundService(context, intent);
}
複製代碼
HeapAnalyzerService 繼承自 ForegroundService , ForegroundService 繼承自 IntentService,runAnalysis方法中就是開啓了一個前臺的IntentService。最後會執行IntentService的onHandleIntent方法,這裏面又執行了抽象方法onHandleIntentInForeground,這個方法在HeapAnalyzerService類中實現。
@Override protected void onHandleIntentInForeground(@Nullable Intent intent) {
if (intent == null) {
CanaryLog.d("HeapAnalyzerService received a null intent, ignoring.");
return;
}
String listenerClassName = intent.getStringExtra(LISTENER_CLASS_EXTRA);
HeapDump heapDump = (HeapDump) intent.getSerializableExtra(HEAPDUMP_EXTRA);
HeapAnalyzer heapAnalyzer =
new HeapAnalyzer(heapDump.excludedRefs, this, heapDump.reachabilityInspectorClasses);
AnalysisResult result = heapAnalyzer.checkForLeak(heapDump.heapDumpFile, heapDump.referenceKey,
heapDump.computeRetainedHeapSize);
AbstractAnalysisResultService.sendResultToListener(this, listenerClassName, heapDump, result);
}
複製代碼
從intent中拿到className和HeapDump,而後經過HeapAnalyzer這個類的checkForLeak方法進行分析。最後經過sendResultToListener方法返回。
public @NonNull AnalysisResult checkForLeak(@NonNull File heapDumpFile, @NonNull String referenceKey, boolean computeRetainedSize) {
long analysisStartNanoTime = System.nanoTime();
if (!heapDumpFile.exists()) {
Exception exception = new IllegalArgumentException("File does not exist: " + heapDumpFile);
return failure(exception, since(analysisStartNanoTime));
}
try {
listener.onProgressUpdate(READING_HEAP_DUMP_FILE);
//將heap文件封裝成MemoryMappedFileBuffer
HprofBuffer buffer = new MemoryMappedFileBuffer(heapDumpFile);
//建立hprof解析器,解析hprof文件
HprofParser parser = new HprofParser(buffer);
listener.onProgressUpdate(PARSING_HEAP_DUMP);
//解析生成快照
Snapshot snapshot = parser.parse();
listener.onProgressUpdate(DEDUPLICATING_GC_ROOTS);
//去除重複的內容
deduplicateGcRoots(snapshot);
listener.onProgressUpdate(FINDING_LEAKING_REF);
//找到泄露對象
Instance leakingRef = findLeakingReference(referenceKey, snapshot);
// False alarm, weak reference was cleared in between key check and heap dump.
if (leakingRef == null) {
String className = leakingRef.getClassObj().getClassName();
return noLeak(className, since(analysisStartNanoTime));
}
//找到泄露對象的最短路徑
return findLeakTrace(analysisStartNanoTime, snapshot, leakingRef, computeRetainedSize);
} catch (Throwable e) {
return failure(e, since(analysisStartNanoTime));
}
}
複製代碼
checkForLeak方法就是LeakCanary中的核心方法了,這裏面用到了Square的另外一個開源庫haha庫,地址 github.com/square/haha
findLeakingReference方法
private Instance findLeakingReference(String key, Snapshot snapshot) {
ClassObj refClass = snapshot.findClass(KeyedWeakReference.class.getName());
if (refClass == null) {
throw new IllegalStateException(
"Could not find the " + KeyedWeakReference.class.getName() + " class in the heap dump.");
}
List<String> keysFound = new ArrayList<>();
for (Instance instance : refClass.getInstancesList()) {
List<ClassInstance.FieldValue> values = classInstanceValues(instance);
Object keyFieldValue = fieldValue(values, "key");
if (keyFieldValue == null) {
keysFound.add(null);
continue;
}
String keyCandidate = asString(keyFieldValue);
if (keyCandidate.equals(key)) {
return fieldValue(values, "referent");
}
keysFound.add(keyCandidate);
}
throw new IllegalStateException(
"Could not find weak reference with key " + key + " in " + keysFound);
}
複製代碼
findLeakTrace方法
private AnalysisResult findLeakTrace(long analysisStartNanoTime, Snapshot snapshot, Instance leakingRef, boolean computeRetainedSize) {
listener.onProgressUpdate(FINDING_SHORTEST_PATH);
ShortestPathFinder pathFinder = new ShortestPathFinder(excludedRefs);
ShortestPathFinder.Result result = pathFinder.findPath(snapshot, leakingRef);
String className = leakingRef.getClassObj().getClassName();
// False alarm, no strong reference path to GC Roots.
if (result.leakingNode == null) {
return noLeak(className, since(analysisStartNanoTime));
}
listener.onProgressUpdate(BUILDING_LEAK_TRACE);
LeakTrace leakTrace = buildLeakTrace(result.leakingNode);
long retainedSize;
if (computeRetainedSize) {
listener.onProgressUpdate(COMPUTING_DOMINATORS);
// Side effect: computes retained size.
snapshot.computeDominators();
Instance leakingInstance = result.leakingNode.instance;
retainedSize = leakingInstance.getTotalRetainedSize();
// TODO: check O sources and see what happened to android.graphics.Bitmap.mBuffer
if (SDK_INT <= N_MR1) {
listener.onProgressUpdate(COMPUTING_BITMAP_SIZE);
retainedSize += computeIgnoredBitmapRetainedSize(snapshot, leakingInstance);
}
} else {
retainedSize = AnalysisResult.RETAINED_HEAP_SKIPPED;
}
return leakDetected(result.excludingKnownLeaks, className, leakTrace, retainedSize,
since(analysisStartNanoTime));
}
複製代碼
經過findPath方法,GCroot開始往下尋找
LeakTrace就是內存泄露的調用棧
getTotalRetainedSize()方法,計算內存泄露的內存空間大小
OK Activity的監控流程就看完啦,下面看一下Fragment的。其實跟Activity差很少。從install開始
public static void install(Context context, RefWatcher refWatcher) {
List<FragmentRefWatcher> fragmentRefWatchers = new ArrayList<>();
// 若是大於Anroid 26,須要增長AndroidOFragmentRefWatcher
if (SDK_INT >= O) {
fragmentRefWatchers.add(new AndroidOFragmentRefWatcher(refWatcher));
}
// 經過反射添加SupportFragmentRefWatcher
try {
Class<?> fragmentRefWatcherClass = Class.forName(SUPPORT_FRAGMENT_REF_WATCHER_CLASS_NAME);
Constructor<?> constructor =
fragmentRefWatcherClass.getDeclaredConstructor(RefWatcher.class);
FragmentRefWatcher supportFragmentRefWatcher =
(FragmentRefWatcher) constructor.newInstance(refWatcher);
fragmentRefWatchers.add(supportFragmentRefWatcher);
} catch (Exception ignored) {
}
if (fragmentRefWatchers.size() == 0) {
return;
}
Helper helper = new Helper(fragmentRefWatchers);
Application application = (Application) context.getApplicationContext();
application.registerActivityLifecycleCallbacks(helper.activityLifecycleCallbacks);
}
複製代碼
經過反射找到SupportFragmentRefWatcher,它類須要在build.gradle中添加debugImplementation 'com.squareup.leakcanary:leakcanary-support-fragment:1.6.3'加入引用。
最後註冊ActivityLifecycleCallbacks,來監聽activity的回調
private final Application.ActivityLifecycleCallbacks activityLifecycleCallbacks =
new ActivityLifecycleCallbacksAdapter() {
@Override public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
for (FragmentRefWatcher watcher : fragmentRefWatchers) {
watcher.watchFragments(activity);
}
}
};
複製代碼
能夠看到這裏監聽的是activity的onActivityCreated這個生命週期函數,而後把當前的activity的對象傳入FragmentRefWatcher中,執行接口watchFragments。SupportFragmentRefWatcher和AndroidOFragmentRefWatcher是FragmentRefWatcher的實現類。最終會回調實現類的watchFragments方法
@Override public void watchFragments(Activity activity) {
FragmentManager fragmentManager = activity.getFragmentManager();
fragmentManager.registerFragmentLifecycleCallbacks(fragmentLifecycleCallbacks, true);
}
複製代碼
經過Activity找到FragmentManager,而後註冊系統的Fragment的生命週期回調監聽
private final FragmentManager.FragmentLifecycleCallbacks fragmentLifecycleCallbacks =
new FragmentManager.FragmentLifecycleCallbacks() {
@Override public void onFragmentViewDestroyed(FragmentManager fm, Fragment fragment) {
View view = fragment.getView();
if (view != null) {
refWatcher.watch(view);
}
}
@Override
public void onFragmentDestroyed(FragmentManager fm, Fragment fragment) {
refWatcher.watch(fragment);
}
};
複製代碼
Fragment的回調主要監聽了onFragmentViewDestroyed和onFragmentDestroyed兩個回調方法。最終都會調用RefWatcher中的watch方法,這裏面跟前面activity中是同樣的啦。