1500字范文,内容丰富有趣,写作好帮手!
1500字范文 > 「Leakcanary 源码分析」看这一篇就够了

「Leakcanary 源码分析」看这一篇就够了

时间:2020-10-27 07:05:44

相关推荐

「Leakcanary 源码分析」看这一篇就够了

image.png

「Leakcanary 」是我们经常用于检测内存泄漏的工具,简单的使用方式,内存泄漏的可视化,是我们开发中必备的工具之一。

分析源码之前

Leakcanary 大神的 github ,最好的老师。

一、使用

1、配置

dependencies {debugImplementation 'com.squareup.leakcanary:leakcanary-android:1.6.3'releaseImplementation 'com.squareup.leakcanary:leakcanary-android-no-op:1.6.3'// Optional, if you use support library fragments:debugImplementation 'com.squareup.leakcanary:leakcanary-support-fragment:1.6.3'}

2、简单使用

public class ExampleApplication extends Application {@Override public void onCreate() {super.onCreate();if (LeakCanary.isInAnalyzerProcess(this)) {// This process is dedicated to LeakCanary for heap analysis.// You should not init your app in this process.return;}LeakCanary.install(this);// Normal app init code...}}

超级简单的配置和使用方式。最后就会得出以下的事例说明。

image.png

二、准备工作

1、Reference

Reference 把内存分为 4 种状态,Active 、 Pending 、 Enqueued 、 Inactive。

Active 一般说来内存一开始被分配的状态都是 ActivePending 快要放入队列(ReferenceQueue)的对象,也就是马上要回收的对象Enqueued 对象已经进入队列,已经被回收的对象。方便我们查询某个对象是否被回收Inactive 最终的状态,无法变成其他的状态。

2、ReferenceQueue

引用队列,在 Reference 被回收的时候,Reference 会被添加到 ReferenceQueue 中

3、如果检测一个对象是否被回收

需要采用 Reference + ReferenceQueue

创建一个引用队列 queue创建 Reference 对象(通常用弱引用)并关联引用队列在 Reference 被回收的时候,Reference 会被添加到 queue 中

//创建一个引用队列 ReferenceQueue queue = new ReferenceQueue(); // 创建弱引用,此时状态为Active,并且Reference.pending为空,// 当前Reference.queue = 上面创建的queue,并且next=null // reference 创建并关联 queueWeakReference reference = new WeakReference(new Object(), queue); // 当GC执行后,由于是弱引用,所以回收该object对象,并且置于pending上,此时reference的状态为PENDING System.gc(); // ReferenceHandler从 pending 中取下该元素,并且将该元素放入到queue中,//此时Reference状态为ENQUEUED,Reference.queue = ReferenceENQUEUED // 当从queue里面取出该元素,则变为INACTIVE,Reference.queue = Reference.NULL Reference reference1 = queue.remove();

在 Reference 类加载的时候,Java 虚拟机会会创建一个最大优先级的后台线程,这个线程的工作就是不断检测 pending 是否为 null,如果不为 null,那么就将它放到 ReferenceQueue。因为 pending 不为 null,就说明引用所指向的对象已经被 GC,变成了不也达。<br /><br />

4、ActivityLifecycleCallbacks

用于监听所有 Activity 生命周期的回调方法。

private final Application.ActivityLifecycleCallbacks lifecycleCallbacks =new Application.ActivityLifecycleCallbacks() {@Override public void onActivityCreated(Activity activity, Bundle savedInstanceState) {}@Override public void onActivityStarted(Activity activity) {}@Override public void onActivityResumed(Activity activity) {}@Override public void onActivityPaused(Activity activity) {}@Override public void onActivityStopped(Activity activity) {}@Override public void onActivitySaveInstanceState(Activity activity, Bundle outState) {}@Override public void onActivityDestroyed(Activity activity) {ActivityRefWatcher.this.onActivityDestroyed(activity);}};

5、Heap Dump

Heap Dump也叫堆转储文件,是一个Java进程在某个时间点上的内存快照。

三、原理说明

1、监听 Activity 的生命周期。<br />2、在 onDestory 的时候,创建对应的 Actitity 的 Refrence 和 相应的 RefrenceQueue,启动后台进程去检测。<br />3、一段时间后,从 RefrenceQueue 中读取,如果有这个 Actitity 的 Refrence,那么说明这个 Activity 的 Refrence 已经被回收,但是如果 RefrenceQueue 没有这个 Actitity 的 Refrence 那就说明出现了内存泄漏。<br />4、dump 出 hprof 文件,找到泄漏路径。

分析源码

程序的唯一入口 LeakCanary.install(this);<br /><br />

1、install

DisplayLeakService 这个类负责发起 Notification 以及将结果记录下来写在文件里面。以后每次启动LeakAnalyzerActivity就从这个文件里读取历史结果,并展示给我们。

public static RefWatcher install(Application application) {return install(application, DisplayLeakService.class);}

public static RefWatcher install(Application application,Class<? extends AbstractAnalysisResultService> listenerServiceClass) {//如果在主线程 那么返回一个无用的 RefWatcher 详解 1.1if (isInAnalyzerProcess(application)) {return RefWatcher.DISABLED;}//把 DisplayLeakActivity 设置为可用 用于显示 DisplayLeakActivity 就是我们看到的那个分析界面enableDisplayLeakActivity(application);// 详解 1.2HeapDump.Listener heapDumpListener =new ServiceHeapDumpListener(application, listenerServiceClass);//详解 2RefWatcher refWatcher = androidWatcher(application, heapDumpListener);详解 3ActivityRefWatcher.installOnIcsPlus(application, refWatcher);return refWatcher;}

1.1 isInAnalyzerProcess

因为 分析的进程是硬外一个独立进程 所以要判断是否是主进程,这个工作需要在 AnalyzerProcess 中进行。

public static boolean isInAnalyzerProcess(Context context) {return isInServiceProcess(context, HeapAnalyzerService.class);}

把App 的进程 和 这个 Service 进程进行对比 。

private static boolean isInServiceProcess(Context context,Class<? extends Service> serviceClass) {PackageManager packageManager = context.getPackageManager();PackageInfo packageInfo;try {packageInfo = packageManager.getPackageInfo(context.getPackageName(), GET_SERVICES);} catch (Exception e) {Log.e("AndroidUtils", "Could not get package info for " + context.getPackageName(), e);return false;}String mainProcess = packageInfo.applicationInfo.processName;ComponentName component = new ComponentName(context, serviceClass);ServiceInfo serviceInfo;try {serviceInfo = packageManager.getServiceInfo(component, 0);} catch (PackageManager.NameNotFoundException ignored) {// Service is disabled.return false;}if (serviceInfo.processName.equals(mainProcess)) {Log.e("AndroidUtils","Did not expect service " + serviceClass + " to run in main process " + mainProcess);// Technically we are in the service process, but we're not in the service dedicated process.return false;}int myPid = android.os.Process.myPid();ActivityManager activityManager =(ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);ActivityManager.RunningAppProcessInfo myProcess = null;for (ActivityManager.RunningAppProcessInfo process : activityManager.getRunningAppProcesses()) {if (process.pid == myPid) {myProcess = process;break;}}if (myProcess == null) {Log.e("AndroidUtils", "Could not find running process for " + myPid);return false;}//把App 的进程 和 这个 Service 进程进行对比 return myProcess.processName.equals(serviceInfo.processName);}

1.2 ServiceHeapDumpListener

设置 DisplayLeakService 和 HeapAnalyzerService 的可用。<br />analyze 方法,开始分析 HeapDump。

public final class ServiceHeapDumpListener implements HeapDump.Listener {private final Context context;private final Class<? extends AbstractAnalysisResultService> listenerServiceClass;public ServiceHeapDumpListener(Context context,Class<? extends AbstractAnalysisResultService> listenerServiceClass) {LeakCanary.setEnabled(context, listenerServiceClass, true);LeakCanary.setEnabled(context, HeapAnalyzerService.class, true);this.listenerServiceClass = checkNotNull(listenerServiceClass, "listenerServiceClass");this.context = checkNotNull(context, "context").getApplicationContext();}@Override public void analyze(HeapDump heapDump) {checkNotNull(heapDump, "heapDump");HeapAnalyzerService.runAnalysis(context, heapDump, listenerServiceClass);}}

2、RefWatcher

private final Executor watchExecutor;private final DebuggerControl debuggerControl;private final GcTrigger gcTrigger;private final HeapDumper heapDumper;private final Set<String> retainedKeys;private final ReferenceQueue<Object> queue;private final HeapDump.Listener heapdumpListener;

watchExecutor:执行内存泄漏检测的 Executor。debuggerControl:用于查询是否在 debug 调试模式下,调试中不会执行内存泄漏检测。gcTrigger:GC 开关,调用系统GC。heapDumper:用于产生内存泄漏分析用的 dump 文件。即 dump 内存 head。retainedKeys:保存待检测和产生内存泄漏的引用的 key。queue:用于判断弱引用持有的对象是否被 GC。heapdumpListener:用于分析 dump 文件,生成内存泄漏分析报告。

这里创建我们所需要的 RefWatcher。

public static RefWatcher androidWatcher(Application app, HeapDump.Listener heapDumpListener) {DebuggerControl debuggerControl = new AndroidDebuggerControl();AndroidHeapDumper heapDumper = new AndroidHeapDumper(app);heapDumper.cleanup();return new RefWatcher(new AndroidWatchExecutor(), debuggerControl, GcTrigger.DEFAULT,heapDumper, heapDumpListener);}

3、ActivityRefWatcher

public static void installOnIcsPlus(Application application, RefWatcher refWatcher) {if (SDK_INT < ICE_CREAM_SANDWICH) {// If you need to support Android < ICS, override onDestroy() in your base activity.return;}ActivityRefWatcher activityRefWatcher = new ActivityRefWatcher(application, refWatcher);activityRefWatcher.watchActivities();}

//注册 lifecycleCallbackspublic void watchActivities() {// Make sure you don't get installed twice.stopWatchingActivities();application.registerActivityLifecycleCallbacks(lifecycleCallbacks);}//ArrayList<ActivityLifecycleCallbacks> mActivityLifecycleCallbacks//就是 mActivityLifecycleCallbacks 的添加public void registerActivityLifecycleCallbacks(ActivityLifecycleCallbacks callback) {synchronized (mActivityLifecycleCallbacks) {mActivityLifecycleCallbacks.add(callback);}}// 注销 lifecycleCallbackspublic void stopWatchingActivities() {application.unregisterActivityLifecycleCallbacks(lifecycleCallbacks);}// //就是 mActivityLifecycleCallbacks 的 移除public void unregisterActivityLifecycleCallbacks(ActivityLifecycleCallbacks callback) {synchronized (mActivityLifecycleCallbacks) {mActivityLifecycleCallbacks.remove(callback);}}

本质就是在 Activity 的 onActivityDestroyed 方法里 执行 refWatcher.watch(activity);

private final Application.ActivityLifecycleCallbacks lifecycleCallbacks =new Application.ActivityLifecycleCallbacks() {@Override public void onActivityCreated(Activity activity, Bundle savedInstanceState) {}@Override public void onActivityStarted(Activity activity) {}@Override public void onActivityResumed(Activity activity) {}@Override public void onActivityPaused(Activity activity) {}@Override public void onActivityStopped(Activity activity) {}@Override public void onActivitySaveInstanceState(Activity activity, Bundle outState) {}@Override public void onActivityDestroyed(Activity activity) {ActivityRefWatcher.this.onActivityDestroyed(activity);}};void onActivityDestroyed(Activity activity) {refWatcher.watch(activity);}

4、watch

public void watch(Object watchedReference, String referenceName) {checkNotNull(watchedReference, "watchedReference");checkNotNull(referenceName, "referenceName");if (debuggerControl.isDebuggerAttached()) {return;}//随机生成 watchedReference 的 key 保证其唯一性final long watchStartNanoTime = System.nanoTime();String key = UUID.randomUUID().toString();retainedKeys.add(key);//这个一个弱引用的子类拓展类 用于 我们之前所说的 watchedReference 和 queue 的联合使用final KeyedWeakReference reference =new KeyedWeakReference(watchedReference, key, referenceName, queue);watchExecutor.execute(new Runnable() {@Override public void run() {//重要方法,确然是否 内存泄漏ensureGone(reference, watchStartNanoTime);}});}

final class KeyedWeakReference extends WeakReference<Object> {public final String key;public final String name;KeyedWeakReference(Object referent, String key, String name,ReferenceQueue<Object> referenceQueue) {super(checkNotNull(referent, "referent"), checkNotNull(referenceQueue, "referenceQueue"));this.key = checkNotNull(key, "key");this.name = checkNotNull(name, "name");}}

5、ensureGone

void ensureGone(KeyedWeakReference reference, long watchStartNanoTime) {long gcStartNanoTime = System.nanoTime();long watchDurationMs = NANOSECONDS.toMillis(gcStartNanoTime - watchStartNanoTime);//把 queue 的引用 根据 key 从 retainedKeys 中引出 。//retainedKeys 中剩下的就是没有分析和内存泄漏的引用的 keyremoveWeaklyReachableReferences();//如果内存没有泄漏 或者处于 debug 模式那么就直接返回if (gone(reference) || debuggerControl.isDebuggerAttached()) {return;}//如果内存依旧没有被释放 那么在 GC 一次gcTrigger.runGc();//再次 清理下 retainedKeysremoveWeaklyReachableReferences();//最后还有 就是说明内存泄漏了 if (!gone(reference)) {long startDumpHeap = System.nanoTime();long gcDurationMs = NANOSECONDS.toMillis(startDumpHeap - gcStartNanoTime);//dump 出 Head 报告File heapDumpFile = heapDumper.dumpHeap();if (heapDumpFile == null) {// Could not dump the heap, abort.return;}long heapDumpDurationMs = NANOSECONDS.toMillis(System.nanoTime() - startDumpHeap);//最后进行分析 这份 HeapDump //LeakCanary 分析内存泄露用的是一个第三方工具 HAHA 别笑 真的是这个名字heapdumpListener.analyze(new HeapDump(heapDumpFile, reference.key, reference.name, watchDurationMs, gcDurationMs,heapDumpDurationMs));}}

private void removeWeaklyReachableReferences() {// WeakReferences are enqueued as soon as the object to which they point to 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);}}private boolean gone(KeyedWeakReference reference) {return !retainedKeys.contains(reference.key);}

6、haha

大家有兴趣可以分析下这个分析库的原理。在这里就不深入研究了。

最后把分析的引用链 写入文件中,发通知。

@TargetApi(HONEYCOMB) @Overrideprotected final void onHeapAnalyzed(HeapDump heapDump, AnalysisResult result) {String leakInfo = leakInfo(this, heapDump, result);Log.d("LeakCanary", leakInfo);if (!result.leakFound || result.excludedLeak) {afterDefaultHandling(heapDump, result, leakInfo);return;}File leakDirectory = DisplayLeakActivity.leakDirectory(this);int maxStoredLeaks = getResources().getInteger(R.integer.__leak_canary_max_stored_leaks);File renamedFile = findNextAvailableHprofFile(leakDirectory, maxStoredLeaks);if (renamedFile == null) {// No file available.Log.e("LeakCanary","Leak result dropped because we already store " + maxStoredLeaks + " leak traces.");afterDefaultHandling(heapDump, result, leakInfo);return;}heapDump = heapDump.renameFile(renamedFile);File resultFile = DisplayLeakActivity.leakResultFile(renamedFile);FileOutputStream fos = null;try {fos = new FileOutputStream(resultFile);ObjectOutputStream oos = new ObjectOutputStream(fos);oos.writeObject(heapDump);oos.writeObject(result);} catch (IOException e) {Log.e("LeakCanary", "Could not save leak analysis result to disk", e);afterDefaultHandling(heapDump, result, leakInfo);return;} finally {if (fos != null) {try {fos.close();} catch (IOException ignored) {}}}PendingIntent pendingIntent =DisplayLeakActivity.createPendingIntent(this, heapDump.referenceKey);String contentTitle =getString(R.string.__leak_canary_class_has_leaked, classSimpleName(result.className));String contentText = getString(R.string.__leak_canary_notification_message);NotificationManager notificationManager =(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);Notification notification;if (SDK_INT < HONEYCOMB) {notification = new Notification();notification.icon = R.drawable.__leak_canary_notification;notification.when = System.currentTimeMillis();notification.flags |= Notification.FLAG_AUTO_CANCEL;notification.setLatestEventInfo(this, contentTitle, contentText, pendingIntent);} else {Notification.Builder builder = new Notification.Builder(this) //.setSmallIcon(R.drawable.__leak_canary_notification).setWhen(System.currentTimeMillis()).setContentTitle(contentTitle).setContentText(contentText).setAutoCancel(true).setContentIntent(pendingIntent);if (SDK_INT < JELLY_BEAN) {notification = builder.getNotification();} else {notification = builder.build();}}notificationManager.notify(0xDEAFBEEF, notification);afterDefaultHandling(heapDump, result, leakInfo);}

总结

其实沿着源码分析很容易让人无法自拔,所以我们更要跳出来看到本质。

1、监听 Activity 的生命周期,在 onDestory 方法里调用 RefWatcher 的 watch 方法。<br />watch 方法监控的是 Activity 对象<br />2、给Activyty 的 Reference 生成唯一性的 key 添加到 retainedKeys 。生成 KeyedWeakReference 对象 ,Activity 的弱引用和 ReferenceQueue 关联。执行 ensureGone 方法。<br />3、如果 retainedKeys 中没有 该 Reference 的 key 那么就说明没有内存泄漏。<br />4、如果有,那么 analyze 分析我们 HeadDump 文件。建立导致泄漏的引用链。<br />5、引用链传递给 APP 进程的 DisplayLeakService,以通知的形式展示出来。<br /><br /><br /><br />

最后

<br />「云开方见日,潮尽炉峰出。」

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。