LeakCanary源码解析

文章目录

  • 1 Java和C/C++语言的内存泄漏
  • 2 垃圾回收机制
    • 2.1 引用计数法
    • 2.2 可达性分析法
  • 3 LeakCanary 1.6.x(1.6.3)
    • 3.1 LeakCanary.install()
      • 3.1.1 LeakCanary.install()源码分析
      • 3.1.2 LeakCanary.install()小结
    • 3.2 RefWatcher.watch()
      • 3.2.1 RefWatcher.watch()源码分析
      • 3.2.2 RefWatcher.watch()小结
    • 3.3 HeapDumper.dumpHeap()
    • 3.4 HeapDumpListener.analyze()
    • 3.5 LeakCanary源码分析总结
  • 4 LeakCanary 2.x(2.2)

1 Java和C/C++语言的内存泄漏

在C/C++语言开发过程中,比如C语言 malloc 分配内存 free 释放内存,C++是 new Object 分配内存 delete object 释放内存,对象的内存分配回收都需要程序员下意识的去维护,否则很容易出现内存泄漏。

但在Java中却没有这种情况,我们使用Java开发很多时候只需要 new Object 创建对象,使用对象后的无效内存都让GC回收了。但也存在GC无法回收的情况导致JVM内存泄漏:长周期的对象引用一直持有着短周期的对象引用,GC以为短周期对象引用还被使用,GC没有回收,所以导致了内存泄漏。

那么,Java是怎么判断对象的引用还被使用?Java垃圾回收机制是怎样的?

2 垃圾回收机制

2.1 引用计数法

引用计数法 简单理解就是记录一个对象被引用的次数,一个引用被使用引用计数器就+1,反之就-1,当引用次数为0就说明是一个垃圾对象可以被回收了。

引用计数法实现非常简单,但也有存在的问题:循环引用,即对象a和对象b各自持有双方的引用,导致GC无法回收,也就导致内存泄漏。

LeakCanary源码解析_第1张图片

2.2 可达性分析法

可达性分析法 根据是否被GC Root引用确认是否是垃圾对象要被GC回收。

可以作为GC Root的对象有:

  • 在线程栈中的局部变量(即正在被调用的方法里面的参数和局部变量)

  • 存活的线程对象

  • JNI的引用

  • Class对象(在Android中Class被加载后是不会被卸载的)

  • 引用类型的静态变量

LeakCanary源码解析_第2张图片

3 LeakCanary 1.6.x(1.6.3)

3.1 LeakCanary.install()

3.1.1 LeakCanary.install()源码分析

LeakCanary 是内存泄漏检测工具,在使用上一句代码即可(使用LeakCanary版本为1.6.3):

public class MyApplication extends Application {

    @Override
    public void onCreate() {
        super.onCreate();
        // LeakCanary是在另外一个进程中启动
        if (LeakCanary.isInAnalyzerProcess(this)) {
            return;
        }
        LeakCanary.install(this);
    }
}

通过一句代码 LeakCanary.install(this) 就可以实现监听内存泄漏。具体看下原理:

LeakCanary.java

public final class LeanCanary {
	public static @NonNull RefWatcher install(@NonNull Application application) {
		return refWatcher(application).listenerServiceClass(DisplayLeakService.class)
			.excludeRefs(AndroidExcludedRefs.createAppDefaults().build()
			.buildAndInstall();
	}

	public static @NonNull AndroidRefWatcherBuilder refWatcher(@NonNull Context context) {
		return new AndroidRefWatcherBuilder(context);
	}
}

AndroidRefWatcherBuilder.java

public final class AndroidRefWatcherBuilder extends RefWatcherBuilder<AndroidRefWatcherBuilder> {
	public @NonNull AndroidRefWatcherBuilder listenerServiceClass(
		@NonNull Class<? extends AbstractAnalysisResultService> listenerServiceClass) {
		enableDisplayLeakActivity = DisplayLeakService.class.isAssignableFrom(listenerServiceClass);
		return headDumpListener(new ServiceHeapDumpListener(context, listenerServiceClass));
	}
}

上面的代码有几点需要说明:

  • DisplayService 是发生内存泄漏时的通知服务

  • excludedRefs() 是排除Android源码出现的内存泄漏问题

最主要的是 AndroidRefWatcherBuilder.buildAndInstall()

AndroidRefWatcherBuilder.java

public final class AndroidRefWatcherBuilder extends RefWatcherBuilder<AndroidRefWatcherBuilder> {
	private boolean watchActivities = true;
	private boolean watchFragments = true;

	public @NonNull RefWatcher buildAndInstall() {
		if (LeakCanaryInternals.installedRefWatcher != null) {
      		throw new UnsupportedOperationException("buildAndInstall() should only be called once.");
    	}
    	RefWatcher refWatcher = build();
    	if (refWatcher != DISABLED) {
    		// 根据app包名生成LeakCanary关联应用
    		if (enableDisplayLeakActivity) {
				LeakCanaryInternals.setEnabledAsync(context, DisplayLeakActivity.class, true);
			}
			// 监听Activity
			if (watchActivities) {
				ActivityRefWatcher.install(context, refWatcher);
			}
			// 监听Fragment
			if (watchFragments) {
				FragmentRefWatcher.Helper.install(context, refWatcher);
			}
		}
		LeakCanaryInternals.installRefWatcher = refWatcher;
		return refWatcher;
	}
} 

LeakCanaryInternals.java

public final class LeakCanaryInternals {
	public static void setEnabledAsync(Context context, final Class<?> componentClass, final boolean enabled) {
		final Context appContext = context.getApplicationContext();
		// 开启子线程处理
		AsyncTask.THREAD_POOL_EXECUTOR.execute(new Runnnable() {
			@Override public void run() {
				setEnabledBlocking(appContext, componentClass, enabled);
			}
		});
	}

	public static void setEnabledBlocking(Context appContext, Class<?> componentClass, boolean enabled) {
		ComponentName component = new ComponentName(appContext, componentClass);
		PackageManager packageManager = appContext.getPackageManager();
		int newState = enabled ? COMPONENT_ENABLED_STATE_ENABLED : COMPONENT_ENABLED_STATE_DISABLED;
		// Blocks on IPC.
		packageManager.setComponentEnabledSetting(component, newState, DONT_KILL_APP);
	}
}

ActivityRefWatcher.java

public final class ActivityRefWatcher {
	public static void install(@NonNull Context context, @NonNull RefWatcher refWatcher) {
		Application application = (Application) context.getApplicationContext();
		ActivityRefWatcher activityRefWatcher = new ActivityRefWatcher(application, refWatcher);

		// 最终是通过在Application注册监听每个Activity的生命周期,然后转发给RefWatcher
		application.registerActivityLifecycleCallbacks(activityRefWatcher.lifecycleCallbacks);
	}

	private final Application.ActivityLifecycleCallbacks lifecycleCallbacks = 
		new ActivityLifecycleCallbacksAdapter() {
			@Override public void onActivityDestroyed(Activity activity) {
				refWatcher.watch(activity);
			}
		}
}

FragmentRefWatcher.java

public interface FragmentRefWatcher {

	final class Helper {
		public static void install(Context context, RefWatcher refWatcher) {
			List<FragmentRefWatcher> fragmentRefWatchers = new ArrayList<>();

			if (SDK_INT >= O) {
				fragmentRefWatchers.add(new AndroidOFragmentRefWatcher(refWatcher));
			}

			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(fragmentRefWatcher);

			// 和Activity一样也是监听Fragment生命周期转发给RefWatcher
			Application application = (Application) context.getApplication();
			application.registerActivityLifecycleCallbacks(helper.activityLifecycleCallbacks);
		}

		private final Application.ActivityLifecycleCallbacks activityLifecycleCallbacks = 
			new ActivityLifecycleCallbacksAdapter() {
			@Override public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
				for (FragmentRefWatcher watcher : fragmentRefWatchers) {
					watcher.watchFragments(activity);
				}
			}
		}
	}
}

AndroidOFragmentRefWatcher.java

class AndroidOFragmentRefWatcher implements FragmentRefWatcher {
	private final RefWatcher refWatcher;

	AndroidOFragmentRefWatcher(RefWatcher refWatcher) {
		this.refWatcher = refWatcher;
	}

	private final FragmentManager.FragmentLifecycleCallbacks fragmentLifecycleCallbacks = 
		new FragmentManager.FragmentLifecycleCallbacks() {
			@Override publci 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);
			}
		}

	@Override public void watchFragments(Activity activity) {
		FragmentManager fragmentManager = activity.getFragmentManager();
		fragmentManager.registerFragmentLifecycleCallbacks(fragmentLifecycleCallbacks, true);
	}
}

上面的处理可以用两个步骤说明:

  • 根据app的包名生成 LeakCanary 的关联应用

  • 通过 Application.registerActivityLifecycleCallbacks() 监听Activity以及 FragmentManager.registerFragmentLifecycleCallbacks() 监听Fragment的生命周期并转发给对应 RefWatcher 处理

这一步生命周期监听处理简单理解就是:

final RefWatcher refWatcher = xxxx;

// 监听Activity生命周期
application.registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacksAdapter() {
	@Override
	public void onActivityDestroyed(Activity activity) {
		watcher.watch(activity);
	}
});

// 监听Fragment生命周期
registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacksAdapter() {
    @Override
    public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
        FragmentManager fm = activity.getFragmentManager();
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            fm.registerFragmentLifecycleCallbacks(new FragmentManager.FragmentLifecycleCallbacks() {
                @Override
                public void onFragmentViewDestroyed(FragmentManager fm, Fragment f) {
                    View view = f.getView();
                    if (view != null) {
                        watcher.watch(view);
                    }
                }

                @Override
                public void onFragmentDestroyed(FragmentManager fm, Fragment f) {
                    watcher.watch(f);
                }
            }, true);
        }
    }
});

3.1.2 LeakCanary.install()小结

总结一下我们调用 LeakCanary.install() 的处理:

  • 创建 AndroidRefWatcherBuilder 构建 LeakCanary 所需参数,如提供 DisplayLeakService 内存泄漏通知服务,排除Android系统源码出现的内存泄漏

  • 创建 RefWatcher,关联app包名生成 LeakCanary 应用,通过 registerXxxLifecycleCallbacks() 监听生命周期转发给 RefWatcher 处理

3.2 RefWatcher.watch()

3.2.1 RefWatcher.watch()源码分析

RefWatcher.java

public final class RefWatcher {
	public void watch(Object watchedReference) {
		watch(watchReference, "");
	}
	
	public void watch(Object watchReference, String referenceName) {
		if (this == DISABLED) {
			return;
		}
		final long watchStartNanoTime = System.nanoTime();
		// 为监听引用watchReference(Activity或Fragment)生成唯一ID
		String key = UUID.randomUUID().toString();
		retainedKeys.add(key);
		// watchReference就是监听的引用对象
		final KeyedWeakReference reference = 
			new KeyedWeakReference(watchReference, key, referenceName, queue);
		ensureGoneAsync(watchStartNanoTime, reference);
	}

	private void ensureGoneAsync(final long watchStartNanoTime, final KeyedWeakReference reference) {
		watchExecutor.execute(new Retryable() {
			@Override public Retryable.Result run() {
				return ensureGone(reference, watchStartNanoTime);
			}
		});
	}
}

上面的代码有一个对象需要注意:KeyedWeakReference

LeakCanary 是通过 KeyedWeakReference 来确认 watchReference(Activity or Fragment) 是否已经被回收的。我们可以通过弱引用的 ReferenceQueue 确认队列中是否有数据,如果有就说明 watchReference 被GC回收了。具体可以看我写的一篇文章:Java的四种引用类型

还有 watchExecutor 又是什么?这需要回到 RefWatcher 被构建时的代码:

RefWatcherBuilder.java

public class RefWatcherBuilder<T extends RefWatcherBuilder<T>> {
	public final RefWatcher build() {
		...

		WatchExecutor watchExecutor = this.watchExecutor;
		if (watchExecutor == null) {
			// AndroidWatchExecutor
			watchExecutor = defaultWatchExecutor();
		}

		...

		return new RefWatcher(watchExecutor, ...);
	}
}

AndroidRefWatcherBuilder.java

public final class AndroidRefWatcherBuilder extends RefWatcherBuilder<AndroidRefWatcherBuilder> {
	return new AndroidWatchExecutor(DEFAULT_WATCH_DELAY_MILLIS);
}

AndroidWatchExecutor.java

public final class AndroidWatchExecutor implements WatchExecutor {
	private final Handler mainHandler;
	private final hnadler backgroundHandler;
	private final long initialDelayMillis;
	private final long maxBackoffFactor;
	
	public AndroidWatchExecutor(long initialDelayMillis) {
		mainHandler = new Handler(Looper.getMainLooper());
		HandlerThread handlerThread = new HandlerThread(LEAK_CANARY_THREAD_NAME);
		handlerThread.start();
		backgroundHandler = new Handler(handlerThread.getLooper());
		this.initialDelayMillis = initialDelayMillis;
		maxBackoffFactor = Long.MAX_VALUE / initialDelayMillis;
	}

	@Override public void execute(@NonNull Retryable retryable) {
		// 第一次进入肯定是主线程,因为RefWatcher是在主线程创建
		if (Looper.getMainLooper().getThread() == Thread.currentThread()) {
			waitForIdle(retryable, 0);
		} else {
			postWaitForIdle(retryable, 0);
		}
	}

	private void postWaitForIdle(final Retryable retryable, final int failedAttempts) {
		mainHandler.post(new Runnable() {
			@Override public void run() {
				waitForIdle(retryable, failedAttempts);
			}
		});
	}

	private void waitForIdle(final Retryable retryable, final int failedAttempts) {
		// This needs to be called from the main thread
		// 保证在主线程处于空闲状态时处理
		// 因为只有主线程生命周期都走完后才有可能正常垃圾回收
		Looper.myQueue().addIdleHandler(new MessageQueue.IdleHandler() {
			@Override public boolean queueIdle() {
				postToBackgroundWithDelay(retryable, failedAttempts);
				return false; // 返回false表示只会执行一次,否则会一直执行
			}
		});
	}

	private void postToBackgroundWithDelay(final Retryable retryable, final int failedAttempts) {
		long exponentialBackoffFactor = (long) Math.min(Math.pow(2, failedAttempts), maxBackoffFactor);
		long delayMillis = initialDelayMillis * exponentialBackoffFactor;
		backgroundHandler.postDelay(new Runnable() {
			@Override public void run() {
				// 在子线程执行Retryable
				Retryable.Result result = retryable.run();
				if (result == RETRY) {
					postWaitForIdle(retryable, failedAttempts + 1);
				}
			}
		}, delayMillis);
	}
}

watchExecutorAndroidWatchExecutor,在 RefWatcher 创建的时候会在子线程执行 Retryable.run()

接下来继续看 Retryable.run() 往后的操作:

public final class RefWatcher {
	Retryable.Result ensureGone(final KeyedWeakReference reference, final long watchStartNanoTime) {
		long gcStartNanoTime = System.nanoTime();
		long watchDurationMs = NANOSECONDS.toMillis(gcStartNanoTime - watchStartNanoTime);

		// 将GC掉的对象从内存泄漏的怀疑名单中移除
		removeWeaklyReachableReference();
	
		if (debuggerControl.isDebuggerAttached()) {
			// The debugger can create false leaks.
			return RETRY;
		}
		// 如果名单没有内存泄漏的引用对象
		// 说明在某次GC已经回收对象,没有内存泄漏,不需要处理
		if (gone(reference)) {
			return DONE;
		}
		// 执行一次GC
		gcTrigger.runGc();

		// 再检查引用对象是否被回收
		removeWeaklyReachableReferences();
		// 引用对象没有被回收还在怀疑名单,说明已经内存泄漏,dump
		if (!gone(reference)) {
			long startDumpHeap = System.nanoTime();
			long gcDurationMs = NANOSECONDS.toMills(startDumpHeap - gcStartNanoTime);

			// 创建dump文件,创建通知提示dump
			File heapDumpFile = heapDumper.dumpHeap();
			// dump文件创建失败,重试
			if (heapDumpFile == RETRY_LATER) {
				// Could not dump the heap.
				return RETRY;
			}
			long heapDumpDurationMs = NANOSECONDS.toMills(System.nanoTime() - startDumpHeap);

			HeapDump heapDump = heapDumpBuilder.heapDumpFile(heapDumpFile).referencekey(reference.key)
				.referenceName(reference.name)
				.watchDurationMs(watchDurationMs)
				.gcDurationMs(gcDurationMs)
				.heapDumpDurationMs(heapDumpDurationMs)
				.build();

			// 分析dump文件
			heapdumpListener.analyze(heapDump);
		}
		return DONE;
	}

	private boolean gone(KeyedWeakReference reference) {
		return !retainedKeys.contains(reference.key);
	}

	private void removeWeaklyReachableReference() {
		// WeakReferences are enqueued as soon as the object to which they point to be becomes weakly
		// reachable. This is before finalization or garbage collection has actually happened.
		KeyedWeakReference ref;
		while ((ref = (KeyedWeakReference) queue.poll()) != null) {
			retainedKeys.remove(ref.key);
		}
	}
}

ensureGone() 主要做的事情:将GC掉的对象从内存泄漏的怀疑名单中移除,如果没有会执行GC后再检查是否从怀疑名单中移除,如果没有说明内存泄漏,创建dump文件,分析dump文件。

3.2.2 RefWatcher.watch()小结

总结一下 RefWatcher.watch() 的处理:

  • 为每一个监听引用对象(Activity或Fragment)提供唯一ID,在主线程空闲走完生命周期时通过 Retryable.run() 检测内存泄漏

  • Retryable.run() 会调用 ensureGone() 检查,如果引用对象仍没有被GC回收仍在 ReferenceQueue 队列说明内存泄漏,创建dump文件

3.3 HeapDumper.dumpHeap()

通过 HeapDumper.dumpHeap() dump文件,那它是怎么dump文件的?继续分析代码:

AndroidHeapDumper.java

public final class AndroidHeapDumper implements HeapDumper {
	@Override @Nullable
	public File dumpHeap() {
		File heapDumpFile = leakDirectoryProvider.newHeapDumpFile();
		
		if (heapDumpFile == RETRY_LATER) {
			return RETRY_LATER;
		}

		FutureResult<Toast> waitingForToast = new FutureResult<>();
		showToast(waitingForToast);

		if (!waitingForToast.wait(5, SECONDS)) {
			CanaryLog.d("Did not dump heap, too much time waiting for Toast.");
			return RETRY_LATER;
		}

		// 创建通知提示dump文件,分析进度会在启动前台分析服务时更新
		Notification.Builder builder = new Notification.Builder(context)
			.setContentTitle(context.getString(R.string.leak_canary_notification_dumping));
		Notification notification = LeakCanaryInternals.buildNotification(context, builder);
		NotificationManager notificationManager = 
			(NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
		int notificationId = (int) SystemClock.uptimeMillis();
		notificationManager.notify(notificationId, notification);

		Toast toast = waitingForToast.get();
		try {
			// 最终使用的是Android提供的工具dump数据到hprof文件
			Debug.dumpHprofData(heapDumpFile.getAbsolutePath());
			cancelToast(toast);
			notificationManager.cancel(notificationId);
		} catch (Exception e) {
			CanaryLog.d(e, "Could not dump heap");
			// Abort heap dump
			return RETRY_LATER;
		}
	}
}

DefaultLeakDirectoryProvider.java

public final class DefaultLeakDirectoryProvider implements LeakDirectoryProvider {
	// 最多7个dump文件
	private static final int DEFAULT_MAX_STORED_HEAP_DUMPS = 7;

	private static final String HPROF_SUFFIX = ".hprof";
	private static final String PENDING_HEAPDUMP_SUFFIX = "_pending" + HPROF_SUFFIX;

	private final int maxStoredHeapDumps;

	public DefaultLeakDirectoryProvider(@NonNull Context context) {
		this(context, DEFAULT_MAX_STORED_HEAP_DUMPS);
	}

	public DefaultLeakDirectoryProvider(@NonNoll Context context, int maxStoredHeapDumps) {
		if (maxStoredHeapDumps < 1) {
			throw new IllegalArgumentException("maxStoredHeapDumps must be at leasts 1");
		}
		this.context = context.getApplicationContext();
		this.maxStoredHeapDumps = maxStoredHeapDumps;
	}

	@Override public @Nullable File newHeapDumpFile() {
		// 从外部存储查找对应的后缀dump文件
		List<File> pendingHeapDumps = listFiles(new FilenameFilter() {
			@Override public boolean accept(File dir, String filename) {
				return filename.endsWith(PENDING_HEAPDUMP_SUFFIX);
			}
		});
		
		...
		
		// 查找到hprof后缀的dump文件,如果dump文件多于7个,删除修改时间较前的hprof文件
		cleanupOldHeapDumps();

		// 检查外部存储权限,如果没有存储权限LeakCanary无法dump文件
		File storageDirectory = externalStorageDirectory();
		if (!directoryWritableAfterMkdirs(storageDirectory)) {
			if (!hasStoragePermission()) {
				CanaryLog.d("WRITE_EXTERNAL_STORAGE permission not granted");
				requestWritePermissionNotification();
			} else {
				String state = Environment.getExternalStorageState();
				if (!Environment.MEDIA_MOUNTED.equals(state)) {
					CanaryLog.d("External storage not mounted. state: %s", state);
				} else {
					CanaryLog.d("Could not create heap dump directory in external storage: [%s]", storageDirectory.getAbsolutePath());
				}
			}
		}
		
		...
		
		return new File(storageDirectory, UUID.randomUUID().toString() + PENDING_HEAPDUMP_SUFFIX);
	}
}

排除权限和dump文件检查外,最主要的是一句代码:Debug.dumpHprofData(),最终是由Android的工具帮助生成hprof文件。

3.4 HeapDumpListener.analyze()

ServiceHeapDumpListener.java

public final class ServiceHeapDumpListener implements HeapDump.Listener {
	@Override public void analyze(@NonNull HeapDump heapDump) {
    	checkNotNull(heapDump, "heapDump");
    	HeapAnalyzerService.runAnalysis(context, heapDump, listenerServiceClass);
  	}
}

HeapAnalyzerService.java

// ForegroundService extends IntentService
public final class HeapAnalyzerService extends ForegroundService
    implements AnalyzerProgressListener {

	public static void runAnalysis(Context context, HeapDump heapDump,
      Class<? extends AbstractAnalysisResultService> listenerServiceClass) {
    	setEnabledBlocking(context, HeapAnalyzerService.class, true);
    	setEnabledBlocking(context, listenerServiceClass, true);
    	// 启动一个前台服务分析,跨进程通信Intent提供分析所需参数
    	Intent intent = new Intent(context, HeapAnalyzerService.class);
   	 	intent.putExtra(LISTENER_CLASS_EXTRA, listenerServiceClass.getName());
    	intent.putExtra(HEAPDUMP_EXTRA, heapDump);
    	ContextCompat.startForegroundService(context, intent);
 	 }	

	// LeakCanary是在另一个进程的,所以这里的分析也是在另一个进程
	@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);
  	}
}

HeapAnalyzer.java

public final class HeapAnalyzer {
	public @NonNull AnalysisResult checkForLeak(@NonNull File heapDumpFile,
      @NonNull String referenceKey,
      boolean computeRetainedSize) {
    	...
      	// 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)); // 没有内存泄漏
      	}
      	// 如果有内存泄漏,查找内存泄漏的引用路径,最终的分析实现LeakCanary是使用haha库实现
      	return findLeakTrace(analysisStartNanoTime, snapshot, leakingRef, computeRetainedSize);
    	...
  	}
}

HeapDumpListener.analyze() 其实就是启动了一个前台服务在其他进程分析内存泄漏的引用路径。

3.5 LeakCanary源码分析总结

在应用启动时,通过 LeakCanary.install() 监听内存泄漏,LeakCanary的处理过程如下:

  • 构建 RetWatcher 提供内存泄漏分析前的相关参数(如 DisplayService 通知服务,excludeRefs() 排除系统源码泄漏),通过 Application.registerXxxLifecycleCallback() 监听Activity或Fragment生命周期转发给 RefWatcher

  • 在Activity或Fragment回调 onDestroy() 时,监听引用对象是否还在 ReferenceQueue 中,有则表示内存泄漏,创建dump文件并通过Android工具 Debug.dumpHprofData() 写入内存泄漏数据,hprof文件将会在另一个前台服务分析

4 LeakCanary 2.x(2.2)

LeakCanary 1.6.x和2.x有不同的一些地方:

  • 2.x版本全部换为使用kotlin编码

  • 内存泄漏引用路径分析库由 haha 库替换为 shark 库,据说减少了90%内存占用,速度提升了6倍

其他的基本原理都和旧版本相同。

内部处理也有所不同:

  • 不需要自己注册 LeakCanary

如果不需要定制配置,新版本 LeakCanary 只需要引入依赖导入即可使用。不再需要在 Application.onCreate() 手动注册 LeakCanaryLeakCanary 自动通过 ContentProvider 注册监听(ContentProvider 会在应用启动前创建):

internal sealed class AppWatcherInstaller : ContentProvider() {
	override fun onCreate(): Boolean {
		val application = context!!.applicationContext as Application
		InternalAppWatcher.install(application)
		return true
	}
}
  • RefWatcher.watch() (kotlin不是 RefWatcher,为了体现两个版本的区别用这个名称方便理解)新旧版本处理不同:

旧版本执行完GC后如果怀疑名单队列中还有监听的引用对象存在内存泄漏会直接dump并分析;在新版本中会存在一个阈值,如果内存泄漏的对象数量在阈值内是不会dump分析。

internal class HeapDumpTrigger(...) {
	private fun checkRetainedObjects(reason: String) {
		...
		var retainedReferenceCount = objectWatcher.retainedObjectCount

		if (retainedReferenceCount > 0) {
			gcTrigger.runGc()
			retainedReferenceCount = objectWatcher.retainedObjectCount
		}

		// 如果内存泄漏对象数量在阈值内,不生成dump文件分析
		if (checkRetainedCount(retainedReferenceCount, config.retainedVisibleThreshold)) return
		
		...
	}

	private fun checkRetainedCount(
		retainedKeysCount: Int,
		retainedVisibleThreshold: Int): Boolean {
		val countChanged = lastDisplayedRetainedObjectCount != retainedKeysCount
		lastDisplayRetainedObjectCount = retainedKeysCount
		if (retainedKeydsCount == 0) {
			...
			return true
		}

		// 引发内存泄漏的对象在阈值内,如果应用在前台会通知提示内存泄漏
		if (retainedKeysCount < retainedVisibleThreshold) {
			if (applicationVisible || applicationInvisibleLessThanWatchPeriod) {
				if (countChanged) { ... }
				// 创建通知
				showRetainedCountNotification(...)
				...
				return true
			}
		}
		return false
	}
}

你可能感兴趣的:(进阶,原理,Android)