LeakCanary源码分析
来源:互联网 发布:java instanceof 编辑:程序博客网 时间:2024/06/14 06:07
LeakCanary是一个监测内测泄露的工具,源码地址 https://github.com/square/leakcanary
Android使用方法:
在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' }
在Application类里引入LeakCanary.install函数
public class ExampleApplication extends Application { @Override public void onCreate() { super.onCreate(); LeakCanary.install(this); }}
当出现内存泄漏时,通知栏会弹出通知,点击之后出现详细的内存泄漏信息activity
具体源码分析:
1.先进入LeakCanary.install函数
/** * Creates a {@link RefWatcher} that works out of the box, and starts watching activity * references (on ICS+). */ public static RefWatcher install(Application application) { return install(application, DisplayLeakService.class, AndroidExcludedRefs.createAppDefaults().build()); } /** * Creates a {@link RefWatcher} that reports results to the provided service, and starts watching * activity references (on ICS+). */ public static RefWatcher install(Application application, Class<? extends AbstractAnalysisResultService> listenerServiceClass, ExcludedRefs excludedRefs) { if (isInAnalyzerProcess(application)) { return RefWatcher.DISABLED; } enableDisplayLeakActivity(application); HeapDump.Listener heapDumpListener = new ServiceHeapDumpListener(application, listenerServiceClass); RefWatcher refWatcher = androidWatcher(application, heapDumpListener, excludedRefs); ActivityRefWatcher.installOnIcsPlus(application, refWatcher); return refWatcher; }
DisplayLeakService.java类负责显示内存泄漏发生的状况,AndroidExcludedRefs.java中可以查看到 一个已知问题的忽略列表,就是说如果发现AndroidExcludedRefs.java类中维护的列表类的内存泄漏,那么在DisplayLeakService并不会显示出来,同时HeapAnalyzer在计算到GC roots的最短强引用路径,也会忽略这些类。
2.isInAnalyzerProcess函数
/** * Whether the current process is the process running the {@link HeapAnalyzerService}, which is * a different process than the normal app process. */ public static boolean isInAnalyzerProcess(Context context) { return isInServiceProcess(context, HeapAnalyzerService.class); }
调用LeakCanaryInternals类的isInServiceProcess函数
public 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) { CanaryLog.d(e, "Could not get package info for %s", context.getPackageName()); 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)) { CanaryLog.d("Did not expect service %s to run in main process %s", serviceClass, 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; List<ActivityManager.RunningAppProcessInfo> runningProcesses = activityManager.getRunningAppProcesses(); if (runningProcesses != null) { for (ActivityManager.RunningAppProcessInfo process : runningProcesses) { if (process.pid == myPid) { myProcess = process; break; } } } if (myProcess == null) { CanaryLog.d("Could not find running process for %d", myPid); return false; } return myProcess.processName.equals(serviceInfo.processName); }
本函数用于检测传入的参数serviceClass所在的进程是否是服务进程,而不是主进程。首先判断packageInfo.applicationInfo.processName是否和serviceClass所在的进程名相等。然后得到当前进程的pid,遍历目前正在运行的进程,判断pid是否相等,得到当前进程的实例对象,判断是否与serviceClass的所在进程名相等,不在则返回false。
isInAnalyzerProcess函数主要用于判断HeapAnalyzerService函数是否运行在服务进程中,不是则不再执行leakCanary.install函数。
3.enableDisplayLeakActivity(application)函数
public static void enableDisplayLeakActivity(Context context) { setEnabled(context, DisplayLeakActivity.class, true); }
使能DisplayLeakActivity.class,在Lancher显示LeakCanary图标
4.接下了创建一个heapDumpListener和refWatcher,具体有什么作用,下面的内容会介绍
5.进入 ActivityRefWatcher.installOnIcsPlus函数
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(); }
new出来一个activityRefWatcher并传入application, refWatcher参数
6.进入activityRefWatcher.watchActivities()
public void watchActivities() { // Make sure you don't get installed twice. stopWatchingActivities(); application.registerActivityLifecycleCallbacks(lifecycleCallbacks); } public void stopWatchingActivities() { application.unregisterActivityLifecycleCallbacks(lifecycleCallbacks); }
这里注册activity生命周期监听函数
7.让我们来看看它在生命周期监听函数里做了什么
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); } };
可以看出关键的函数在onActivityDestroyed监听onDestory()函数里
8.进入 ActivityRefWatcher.this.onActivityDestroyed(activity)
void onActivityDestroyed(Activity activity) { refWatcher.watch(activity); }
进入关键函数了,LeakCanary在每个Activity的生命周期onDestory函数里对activity对象进行监听,以此确实这个acitivity是否存在内存泄漏的问题。refWatcher就是刚才传入的RefWatcher实例。
9.进入到生成RefWatcher的LeakCanary.androidWatcher函数
/** * Creates a {@link RefWatcher} with a default configuration suitable for Android. */ public static RefWatcher androidWatcher(Context context, HeapDump.Listener heapDumpListener, ExcludedRefs excludedRefs) { LeakDirectoryProvider leakDirectoryProvider = new DefaultLeakDirectoryProvider(context); DebuggerControl debuggerControl = new AndroidDebuggerControl(); AndroidHeapDumper heapDumper = new AndroidHeapDumper(context, leakDirectoryProvider); heapDumper.cleanup(); Resources resources = context.getResources(); int watchDelayMillis = resources.getInteger(R.integer.leak_canary_watch_delay_millis); AndroidWatchExecutor executor = new AndroidWatchExecutor(watchDelayMillis); return new RefWatcher(executor, debuggerControl, GcTrigger.DEFAULT, heapDumper, heapDumpListener, excludedRefs); }
androidWatcher函数在这里new出来了debuggerControl,heapDumper,executor这些对象,这些对象是做什么的,接下来有介绍,把这些参数传进new出来的RefWatcher的对象里,这个RefWatcher对象就是第8步里的refWatcher对象,接下来我们看看第8步里具体的refWatcher.watch执行过程
10.RefWatcher.watch函数,从这个函数来分析监听对象是否能被回收
/** * Identical to {@link #watch(Object, String)} with an empty string reference name. * * @see #watch(Object, String) */ public void watch(Object watchedReference) { watch(watchedReference, ""); } /** * Watches the provided references and checks if it can be GCed. This method is non blocking, * the check is done on the {@link Executor} this {@link RefWatcher} has been constructed with. * * @param referenceName An logical identifier for the watched object. */ public void watch(Object watchedReference, String referenceName) { checkNotNull(watchedReference, "watchedReference"); checkNotNull(referenceName, "referenceName"); if (debuggerControl.isDebuggerAttached()) { return; } final long watchStartNanoTime = System.nanoTime(); String key = UUID.randomUUID().toString(); retainedKeys.add(key); final KeyedWeakReference reference = new KeyedWeakReference(watchedReference, key, referenceName, queue); watchExecutor.execute(new Runnable() { @Override public void run() { ensureGone(reference, watchStartNanoTime); } }); }
debuggerControl就是第9步new出来的AndroidDebuggerControl,其isDebuggerAttached()的实现就是判断是不是在连接了debug调试。如果连接了debug调试,则停止watch。
生成对一个Refercen产生一个唯一的key对象,系统nanoTime的watchStartNanoTime对象,并把key对象加入一个叫retainedKeys的全局Set集。并且将传入此函数的watchedReference,referenceName以及key,queue对象传入KeyedWeakReference生成一个名为reference的弱引用对象。
最后在线程池里运行ensureGone函数,ensureGone函数的参数是reference和watchStartNanoTime对象,线程池为第9步new出来的AndroidWatchExecutor对象
11.ensureGone函数
void ensureGone(KeyedWeakReference reference, long watchStartNanoTime) { long gcStartNanoTime = System.nanoTime(); long watchDurationMs = NANOSECONDS.toMillis(gcStartNanoTime - watchStartNanoTime); removeWeaklyReachableReferences(); if (gone(reference) || debuggerControl.isDebuggerAttached()) { return; } gcTrigger.runGc(); removeWeaklyReachableReferences(); if (!gone(reference)) { long startDumpHeap = System.nanoTime(); long gcDurationMs = NANOSECONDS.toMillis(startDumpHeap - gcStartNanoTime); File heapDumpFile = heapDumper.dumpHeap(); if (heapDumpFile == HeapDumper.NO_DUMP) { // Could not dump the heap, abort. return; } long heapDumpDurationMs = NANOSECONDS.toMillis(System.nanoTime() - startDumpHeap); heapdumpListener.analyze( new HeapDump(heapDumpFile, reference.key, reference.name, excludedRefs, watchDurationMs, gcDurationMs, heapDumpDurationMs)); } }
函数首先调用removeWeaklyReachableReferences函数,移除retainedKeys中所有包含所有queue全局变量key的子项,然后使用gone函数判断引用对象是否能被gc,如果没有,使用gcTrigger.runGc()强制执行一边gc,在调用 removeWeaklyReachableReferences(),再次调用gone函数,让我们来看看removeWeaklyReachableReferences和gone函数的源码
private boolean gone(KeyedWeakReference reference) { return !retainedKeys.contains(reference.key); } 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); } }
为什么将queue里面的弱引用的key在retainedKeys移除,然后判断retainedKeys是否包含这个key就可以判断这个引用是否能被gc呢?
这是LeakCanary的核心原理,关键在于queue这个全局变量
private final ReferenceQueue<Object> queue; public RefWatcher(Executor watchExecutor, DebuggerControl debuggerControl, GcTrigger gcTrigger, ...... queue = new ReferenceQueue<>(); }
第10步将queue作为KeyedWeakReference的构造参数传入,ReferenceQueue是作为 JVM GC与上层Reference对象管理之间的一个消息传递方式, 软引用、弱引用等的入队操作有vm的gc直接操作,retainedKeys 代表没被gc 回收的对象, 而queue中的弱引用代表的是被gc了的对象,通过这两个结构监控对象是不是被回收了。retainedKeys存放了RefWatcher为每个被监控的对象生成的唯一key。同时每个被监控对象的弱引用(KeyedWeakReference)关联了 其对应的key 和 queue,这样对象若被回收,则其对应的弱引用会被入队到queue中。removeWeaklyReachableReferences(..)所做的就是把存在与queue中的弱引用的key 从 retainedKeys 中删除。这样在gone函数中检测retainedKeys是否包含key就能检测出是否被gc了。
接下来,使用heapDumper.dumpHeap()产生一个dump文件,heapDumper为第9步生成的new AndroidHeapDumper的heapDumper参数其具体实现将Debug.dumpHprofData的数据保存到文件里。
接下来使用 heapdumpListener.analyze对dump文件进行分析,heapdumpListener为第一步里面的ServiceHeapDumpListener类。
12.进入ServiceHeapDumpListener.analyze函数
@Override public void analyze(HeapDump heapDump) { checkNotNull(heapDump, "heapDump"); HeapAnalyzerService.runAnalysis(context, heapDump, listenerServiceClass); }
进入到HeapAnalyzerService.runAnalysis中
public static void runAnalysis(Context context, HeapDump heapDump, Class<? extends AbstractAnalysisResultService> listenerServiceClass) { Intent intent = new Intent(context, HeapAnalyzerService.class); intent.putExtra(LISTENER_CLASS_EXTRA, listenerServiceClass.getName()); intent.putExtra(HEAPDUMP_EXTRA, heapDump); context.startService(intent); }
调用HeapAnalyzerService解析dump文件,HeapAnalyzerService在另外一个进程空间里运行,来看AndroidManifest文件
<service android:name=".internal.HeapAnalyzerService" android:process=":leakcanary" android:enabled="false" />
android:process表明其在单独的leakcanary进程运行
HeapAnalyzerService里的具体操作函数
@Override protected void onHandleIntent(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); AnalysisResult result = heapAnalyzer.checkForLeak(heapDump.heapDumpFile, heapDump.referenceKey); AbstractAnalysisResultService.sendResultToListener(this, listenerClassName, heapDump, result); }
heapAnalyzer.checkForLeak对其进行具体分析
AbstractAnalysisResultService.sendResultToListener将结果抛出。
12.进入heapAnalyzer.checkForLeak
/** * Searches the heap dump for a {@link KeyedWeakReference} instance with the corresponding key, * and then computes the shortest strong reference path from that instance to the GC roots. */ public AnalysisResult checkForLeak(File heapDumpFile, String referenceKey) { long analysisStartNanoTime = System.nanoTime(); if (!heapDumpFile.exists()) { Exception exception = new IllegalArgumentException("File does not exist: " + heapDumpFile); return failure(exception, since(analysisStartNanoTime)); } try { HprofBuffer buffer = new MemoryMappedFileBuffer(heapDumpFile); HprofParser parser = new HprofParser(buffer); Snapshot snapshot = parser.parse(); deduplicateGcRoots(snapshot); Instance leakingRef = findLeakingReference(referenceKey, snapshot); // False alarm, weak reference was cleared in between key check and heap dump. if (leakingRef == null) { return noLeak(since(analysisStartNanoTime)); } return findLeakTrace(analysisStartNanoTime, snapshot, leakingRef); } catch (Throwable e) { return failure(e, since(analysisStartNanoTime)); } }
这一部分包含很多函数,主要是使用haha这库对dump文件进行分析,分析其中是否有还可达的对象,这一部分不在这里具体分析,想要弄清楚的童鞋去看源码吧。
13.当分析出结果后,使用AbstractAnalysisResultService.sendResultToListener抛出结果
public static void sendResultToListener(Context context, String listenerServiceClassName, HeapDump heapDump, AnalysisResult result) { Class<?> listenerServiceClass; try { listenerServiceClass = Class.forName(listenerServiceClassName); } catch (ClassNotFoundException e) { throw new RuntimeException(e); } Intent intent = new Intent(context, listenerServiceClass); intent.putExtra(HEAP_DUMP_EXTRA, heapDump); intent.putExtra(RESULT_EXTRA, result); context.startService(intent); }
使用intent把结果抛给主进程,其中的listenerServiceClassName的service就是传入ServiceHeapDumpListener的DisplayLeakService.class的name
14.DisplayLeakService里对传过来的结果的处理函数
@Override protected final void onHeapAnalyzed(HeapDump heapDump, AnalysisResult result) { String leakInfo = leakInfo(this, heapDump, result, true); CanaryLog.d(leakInfo); boolean resultSaved = false; boolean shouldSaveResult = result.leakFound || result.failure != null; if (shouldSaveResult) { heapDump = renameHeapdump(heapDump); resultSaved = saveResult(heapDump, result); } PendingIntent pendingIntent; String contentTitle; String contentText; if (!shouldSaveResult) { contentTitle = getString(R.string.leak_canary_no_leak_title); contentText = getString(R.string.leak_canary_no_leak_text); pendingIntent = null; } else if (resultSaved) { pendingIntent = DisplayLeakActivity.createPendingIntent(this, heapDump.referenceKey); if (result.failure == null) { String size = formatShortFileSize(this, result.retainedHeapSize); String className = classSimpleName(result.className); if (result.excludedLeak) { contentTitle = getString(R.string.leak_canary_leak_excluded, className, size); } else { contentTitle = getString(R.string.leak_canary_class_has_leaked, className, size); } } else { contentTitle = getString(R.string.leak_canary_analysis_failed); } contentText = getString(R.string.leak_canary_notification_message); } else { contentTitle = getString(R.string.leak_canary_could_not_save_title); contentText = getString(R.string.leak_canary_could_not_save_text); pendingIntent = null; } showNotification(this, contentTitle, contentText, pendingIntent); afterDefaultHandling(heapDump, result, leakInfo); }
这个函数里主要是判断传来的结果有没有找到内存泄漏,如果找到的话把泄漏信息保存到文件,然后使用DisplayLeakActivity.createPendingIntent生成pendingIntent,然后发生通知到系统,DisplayLeakActivity.createPendingIntent里指明了点击通知会进入DisplayLeakActivity,DisplayLeakActivity会显示泄漏的一些具体信息。
LeakCanary的主要内容,到这里就分析完了,有什么错漏请大家批评指正。
- LeakCanary源码分析
- LeakCanary源码分析
- LeakCanary源码分析
- LeakCanary源码分析
- LeakCanary源码分析第一讲
- 内存泄露检测神器 -- LeakCanary源码分析
- LeakCanary从入门到源码分析
- [源码]LeakCanary
- LeakCanary源码分析第二讲-RefWatcher详解
- LeakCanary源码分析第二讲-RefWatcher详解
- (4.2.39)内存泄漏检测LeakCanary源码分析
- 《android源码分析系列》LeakCanary- 如何检测 Activity 是否泄漏
- LeakCanary源码学习
- LeakCanary框架源码解析
- LeakCanary分析内存泄漏
- LeakCanary原理分析
- Android-LeakCanary原理分析
- 性能分析-内存分析leakcanary
- luogu1151 亲戚
- 【例题】【线段树】
- 特别注意用UpdateData(FALSE)还是SetDlgItemText(IDC_EDIT_Coordinate, m_strCoor);
- POJ2352 Stars[树状数组]
- c语言,字符串转换成整数
- LeakCanary源码分析
- ReentrantLock介绍和实现
- UOJ #82. 【UR #7】水题生成器
- Servlet的监听器Listener
- (巩固基础篇)排序算法:①插入排序②希尔排序③冒泡排序④选择排序⑤快速排序
- Codeforces Round #336 (Div. 2) B 暴力
- C语言中关于内存这个话题
- UOJ #113. 【UER #2】手机的生产
- 操作系统内存管理——分区、页式、段式管理