版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、Android 源码系列之从源码的角度深入理解LeakCanary的内存泄露检测机制(下)LeakCanary的相关配置信息。根据上篇文章的介绍我们知道LeakCanary为了不给APP进程造成影响所以新开启了一个进程,在新开启的进程中做内存泄露检测,这篇文章将要带领小伙伴们从源码的角度出发深入了解一下LeakCanary的内存泄露检测机制,希望能给小伙伴们一点帮助,如果你对LeakCanary的原理非常熟悉了,请跳过本文(*_*) 当在项目中引入了LeakCanary后,就是进行LeakCanary的安装操作,初始化操作流程如下所示:java view plain copypublic cl
2、ass ExampleApplication extends Application Override public void onCreate() super.onCreate(); / 判断是否是LeakCanary的分析进程 if (LeakCanary.isInAnalyzerProcess(this) / This process is dedicated to LeakCanary for heap analysis. / You should not init your app in this process. return; / 初始化LeakCanary LeakCanary
3、.install(this); 安装LeakCanary前先判断当前进程是否是HeapAnalyzerService所在的远程分析进程,如果是分析进程就直接返回因为分析进程只是来分析堆信息是否存在内存泄露,否则调用LeakCanary的install()方法,该方法就是入口,我们跟进去,源码如下:java view plain copy/* * Creates a link RefWatcher that works out of the box, and starts watching activity * references (on ICS+). */ public static Ref
4、Watcher install(Application application) return refWatcher(application).listenerServiceClass(DisplayLeakService.class) .excludedRefs(AndroidExcludedRefs.createAppDefaults().build() .buildAndInstall(); install()方法内部通过链式调用最终返回了一个RefWatcher对象,该对象就是来监听哪些对象是否发生内存泄露的,refWatcher()方法返回AndroidRefWatcherBuild
5、er对象以后都是调用该对象的链式方法,其中excludeRefs()方法表示排除掉由Android SDK引发的内存泄露,因为Android SDK引发的内存泄露并非我们程序造成如果检测到是Android SDK引发的就不会报告给用户(如果想详细了解SDK引发的内存泄露信息可自行查看AndroidExcludedRefs类,该类有详细说明)。最后调用的是buildAndInstall()方法,该方法源码如下:java view plain copy/* * Creates a link RefWatcher instance and starts watching activity refer
6、ences (on ICS+). */ public RefWatcher buildAndInstall() RefWatcher refWatcher = build(); if (refWatcher != DISABLED) LeakCanary.enableDisplayLeakActivity(context); ActivityRefWatcher.installOnIcsPlus(Application) context, refWatcher); return refWatcher; buildAndInstall()方法中调用build()方法获取一个RefWatcher对
7、象refWatcher并最终把refWatcher返回,当时在返回之前做了一个判断,若refWatcher不是DISABLED对象时就先后调用了LeakCanary的静态enableDisplayLeakActivity()方法和ActivityRefWatcher的静态installOnIcsPlus()方法。为什么要判断refWatcher对象是不是DISABLED呢?是因为在打包的时候如果当前模式是DEBUG模式那么refWatcher就不是DISABLED,否则就是DISABLED。这是间接的判断打包模式来决定时候安装LeakCanary库。 LeakCanary的静态方法enable
8、DisplayLeakActivity()是动态的设定DisplayLeakActivity为可用状态,因为在上篇文章中介绍过DisplayLeakActivity默认是不可用的。ActivityRefWatcher的installOnIcsPlus()方法是真正的安装LeakCanary库的,该方法源码如下:java view plain copy 在CODE上查看代码片派生到我的代码片public static void installOnIcsPlus(Application application, RefWatcher refWatcher) if (SDK_INT ICE_CREA
9、M_SANDWICH) / 如果当前SDK版本号小于4.0则直接返回 / If you need to support Android ICS, override onDestroy() in your base activity. return; ActivityRefWatcher activityRefWatcher = new ActivityRefWatcher(application, refWatcher); activityRefWatcher.watchActivities(); installOnIcsPlus()方法先判断当前SDK版本号,如果SDK版本号低于4.0则什么
10、都不做就直接返回了,那也就是说LeakCanary库在Android4.0之前是没法直接使用的,若要在4.0之前使用就需要在基类BaseActivity的onDestroy()方法中调用RefWatcher的watch()方法来监控。接着是创建了一个ActivityRefWatcher实例对象并调用该对象的watchActivities()方法,根据方法名字就知道是监控Activity的,跟进源码看一下该方法,源码如下:java view plain copy 在CODE上查看代码片派生到我的代码片public void watchActivities() / Make sure you do
11、nt get installed twice. / 开始监控Activity之前先尝试移除已经添加过的回调,确保只添加一次 stopWatchingActivities(); application.registerActivityLifecycleCallbacks(lifecycleCallbacks); public void stopWatchingActivities() / 移除Application中已经添加的回调接口 application.unregisterActivityLifecycleCallbacks(lifecycleCallbacks); 通过阅读watchAc
12、tivities()方法发现原来LeakCanary是巧妙的利用了Android 4.0之后的API,因为在4.0之后Google给Application添加了Activity的生命周期回调接口,如果我们注入了该回调接口,那么当Activity的声明周期发生变化的时候就会回调相关方法。我们看一下注入的回调接口lifecycleCallbacks,源码如下:java view plain copy 在CODE上查看代码片派生到我的代码片private final Application.ActivityLifecycleCallbacks lifecycleCallbacks = new App
13、lication.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) Over
14、ride public void onActivityStopped(Activity activity) Override public void onActivitySaveInstanceState(Activity activity, Bundle outState) Override public void onActivityDestroyed(Activity activity) / 每当Activity销毁时系统都会回调该方法 ActivityRefWatcher.this.onActivityDestroyed(activity); ; 在lifecycleCallbacks
15、()回调方法中仅仅在onActivityDestroyed()方法中回调了ActivityRefWatcher的onActivityDestroyed()方法并把要销毁的Activity传递进来,因此我们可以猜测当Activity被销毁的时候都会触发onActivityDestroyed()方法,LeakCanary就是通过监控传递进来的Activity通过判断它的应用关系来检测是否是发生了内存泄露。 到这里我们已经知道了LeakCanary检测Activity的时机,接下来看看LeakCanary是如何判断Activity存在内存泄露的。onActivityDestroyed()方法就是检测
16、Activity是否发生内存泄露的,其源码如下:java view plain copy 在CODE上查看代码片派生到我的代码片void onActivityDestroyed(Activity activity) refWatcher.watch(activity); onActivityDestroyed()方法中辗转调用了refWatcher的watch()方法(该方法可以监控所有的Java对象),watch()方法又调用其重载方法,代码如下:java view plain copy 在CODE上查看代码片派生到我的代码片/* * Watches the provided referen
17、ces and checks if it can be GCed. This method is non blocking, * the check is done on the link WatchExecutor this link RefWatcher has been constructed * with. * * param referenceName An logical identifier for the watched object. */ public void watch(Object watchedReference, String referenceName) if
18、(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(wat
19、chedReference, key, referenceName, queue); ensureGoneAsync(watchStartNanoTime, reference); watch()方法中先是校验当前程序是否是DEBUG模式,如果是就直接返回否则对传递进来的参数进行非空校验,校验通过之后通过生成一个唯一的UUID并存储在retainKeys中,然后根据这个UUID初始化一个KeyedWeakReference实例对象reference,reference中保留了当前UUID和name,然后调用了ensureGoneAsync()方法,该方法源码如下:java view plain
20、 copy 在CODE上查看代码片派生到我的代码片private void ensureGoneAsync(final long watchStartNanoTime, final KeyedWeakReference reference) watchExecutor.execute(new Retryable() Override public Retryable.Result run() return ensureGone(reference, watchStartNanoTime); ); ensureGoneAsync()方法中调用了watchExecutor的execute()方法,
21、经过分析watchExecutor是AndroidWatchExecutor的实例对象,也就是说最终执行的是AndroidWatchExecutor的execute()方法,我们进入该方法看看:java view plain copy 在CODE上查看代码片派生到我的代码片Override public void execute(Retryable retryable) / 确保在主线程中执行 if (Looper.getMainLooper().getThread() = Thread.currentThread() waitForIdle(retryable, 0); else postW
22、aitForIdle(retryable, 0); 在AndroidWatchExecutor的execute()方法中首先判断当前线程是不是主线程,如果是主线程就直接调用waitForIdle()方法,否则调用postWaitForIdle()方法切换到主线后再执行waitForIdle()方法。由此可见execute()方法是保证操作在主线程中进行,我们进入waitForIdle()方法看一下,源码如下所示:java view plain copy 在CODE上查看代码片派生到我的代码片void waitForIdle(final Retryable retryable, final in
23、t 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; ); waitForIdle()方法中通过调用Looper的myQueue()方法拿到当前主线程的消息
24、队列MessageQueue对象,接着调用MessageQueue对象的addIdleHandler()方法给当前消息队列添加一个IdleHandler实例对象,IdleHandler是一个接口,该接口中定义的queueIdle()方法的返回值为boolean类型,当返回true表示继续保持IdleHandler实例对象否则移除该实例对象。需要说明的是:addIdleHandler()方法的目的是给当前消息队列添加一个回调接口,当当前消息队列中没有可执行的消息时就会回调该IdleHandler的queueIdle()方法,说白了就是等到主线程空闲的时候就会回调queueIdle()方法,que
25、ueIdle()方法返回了false表明该方法只被执行一次。在queueIdle()方法中调用了postToBackgroundWithDelay()方法,我们跟进postToBackgroundWithDelay()方法中看一下,该方法源码如下:java view plain copy 在CODE上查看代码片派生到我的代码片private void postToBackgroundWithDelay(final Retryable retryable, final int failedAttempts) long exponentialBackoffFactor = (long) Math.
26、min(Math.pow(2, failedAttempts), maxBackoffFactor); long delayMillis = initialDelayMillis * exponentialBackoffFactor; backgroundHandler.postDelayed(new Runnable() Override public void run() / 切换到子线程中执行,retryable.run()方法 Retryable.Result result = retryable.run(); if (result = RETRY) / 如果retryable的run
27、方法返回类型为RETRY,则循环以上流程 postWaitForIdle(retryable, failedAttempts + 1); , delayMillis); postToBackgroundWithDelay()方法又切换到切换到子线程中执行Retryable的run()方法,如果run()方法返回值为RETRY就继续调用postWaitForIdle()方法循环以上流程否则本次调用结束,由于Retryable的run()方法中调用的是RefWatcher的ensureGone()方法,我们进入该方法看一下,其源码如下所示:java view plain copy 在CODE上查看
28、代码片派生到我的代码片Retryable.Result ensureGone(final KeyedWeakReference reference, final long watchStartNanoTime) long gcStartNanoTime = System.nanoTime(); long watchDurationMs = NANOSECONDS.toMillis(gcStartNanoTime - watchStartNanoTime); / 尝试移除 removeWeaklyReachableReferences(); if (debuggerControl.isDebug
29、gerAttached() / The debugger can create false leaks. return RETRY; / gone()方法检测是否包含当前reference if (gone(reference) return DONE; / 触发垃圾回收器进行回收操作 gcTrigger.runGc(); / 再次尝试移除操作 removeWeaklyReachableReferences(); / 若gone()方法返回false,表示有内存泄露 if (!gone(reference) long startDumpHeap = System.nanoTime(); lon
30、g gcDurationMs = NANOSECONDS.toMillis(startDumpHeap - gcStartNanoTime); / 把堆内存相关信息映射成文件 File heapDumpFile = heapDumper.dumpHeap(); if (heapDumpFile = RETRY_LATER) / Could not dump the heap. return RETRY; long heapDumpDurationMs = NANOSECONDS.toMillis(System.nanoTime() - startDumpHeap); / 分析堆内存的映射文件,
31、判断是否发生内存泄露 heapdumpListener.analyze( new HeapDump(heapDumpFile, reference.key, , excludedRefs, watchDurationMs, gcDurationMs, heapDumpDurationMs); return DONE; 在ensureGone()方法中先尝试从缓存队列中移除相应软引用,然后再调用gone()方法检测缓存集合中是否还包含传递进来的弱引用,如果还包含就表明可能发生了内存泄露,这时候调用gcTrigger.runGc()操作出发垃圾回收器进行内存回收,之后再
32、调用gone()方法进行检测,如果gone()方法依然返回false就调用heapDumper的dumpHeap()方法把堆内存映射成文件,接着就是启动调用heapdumpListener的analyze()方法来分析堆映射文件,源码如下:java view plain copy 在CODE上查看代码片派生到我的代码片Override public void analyze(HeapDump heapDump) checkNotNull(heapDump, heapDump); HeapAnalyzerService.runAnalysis(context, heapDump, listene
33、rServiceClass); analyze()方法中直接调用了HeapAnalyzerService的静态方法runAnalysis(),HeapAnalyzerService是IntentService的子类,其主要特点是执行完毕后会自行销毁,如果你对IntentService不熟悉建议阅读一下我先前写的文章Android 源码系列之从源码的角度深入理解IntentService及HandlerThread,在这篇文章中有对IntentService做详细解说。我们看一下HeapAnalyzerService的源码,如下所示:java view plain copy 在CODE上查看代码
34、片派生到我的代码片/* * This service runs in a separate process to avoid slowing down the app process or making it run * out of memory. */ public final class HeapAnalyzerService extends IntentService private static final String LISTENER_CLASS_EXTRA = listener_class_extra; private static final String HEAPDUMP_
35、EXTRA = heapdump_extra; public static void runAnalysis(Context context, HeapDump heapDump, Class listenerServiceClass) Intent intent = new Intent(context, HeapAnalyzerService.class); intent.putExtra(LISTENER_CLASS_EXTRA, listenerServiceClass.getName(); intent.putExtra(HEAPDUMP_EXTRA, heapDump); / 启动
36、HeapAnalyzerService服务 context.startService(intent); public HeapAnalyzerService() super(HeapAnalyzerService.class.getSimpleName(); Override protected void onHandleIntent(Intent intent) if (intent = null) CanaryLog.d(HeapAnalyzerService received a null intent, ignoring.); return; String listenerClassN
37、ame = intent.getStringExtra(LISTENER_CLASS_EXTRA); HeapDump heapDump = (HeapDump) intent.getSerializableExtra(HEAPDUMP_EXTRA); / 初始化HeapAnalyzer实例对象heapAnalyzer HeapAnalyzer heapAnalyzer = new HeapAnalyzer(heapDump.excludedRefs); / 调用heapAnalyzer对象的checkForLeak()方法检测内存泄露 AnalysisResult result = heap
38、Analyzer.checkForLeak(heapDump.heapDumpFile, heapDump.referenceKey); / 把检测结果传送给AbstractAnalysisResultService AbstractAnalysisResultService.sendResultToListener(this, listenerClassName, heapDump, result); HeapAnalyzerService的静态runAnalysis()方法启动了HeapAnalyzerService服务,该服务启动后会调用onHandleIntent()方法,在onHan
39、dleIntent()方法执行完毕后HeapAnalyzerService服务的生命周期也就结束了。在onHandleIntent()方法中通过HeapAnalyzer对象的checkForLeak()方法来检测分析是否发生了内存泄露。我们看一下checkForLeak()方法,其源码如下:java view plain copy 在CODE上查看代码片派生到我的代码片/* * Searches the heap dump for a link KeyedWeakReference instance with the corresponding key, * and then computes
40、 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: + h
41、eapDumpFile); return failure(exception, since(analysisStartNanoTime); try / 根据内存映射文件生成一个HprofBuffer对象 HprofBuffer buffer = new MemoryMappedFileBuffer(heapDumpFile); / 根据HprofBuffer对象生成一个解析器parser HprofParser parser = new HprofParser(buffer); / 解析内存映射文件并生成相应快照 Snapshot snapshot = parser.parse(); / 核查
42、引用路径 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(analysisStar
43、tNanoTime, snapshot, leakingRef); catch (Throwable e) return failure(e, since(analysisStartNanoTime); 在checkForLeak()方法中主要是利用square的另外一个开源库HAHA来做内存检测的,该库的GitHub地址为:java view plain copy 在CODE上查看代码片派生到我的代码片public abstract class AbstractAnalysisResultService extends IntentService private static final S
44、tring HEAP_DUMP_EXTRA = heap_dump_extra; private static final String RESULT_EXTRA = result_extra; public static void sendResultToListener(Context context, String listenerServiceClassName, HeapDump heapDump, AnalysisResult result) Class listenerServiceClass; try / 加载listenerServiceClassName类类,listene
45、rServiceClassName为DisplayLeakService listenerServiceClass = Class.forName(listenerServiceClassName); catch (ClassNotFoundException e) throw new RuntimeException(e); / 由于DisplayLeakService是AbstractAnalysisResultService的实现类 Intent intent = new Ient(context, listenerServiceClass); intent.putExtra(HEAP_
46、DUMP_EXTRA, heapDump); intent.putExtra(RESULT_EXTRA, result); context.startService(intent); public AbstractAnalysisResultService() super(AbstractAnalysisResultService.class.getName(); Override protected final void onHandleIntent(Intent intent) HeapDump heapDump = (HeapDump) intent.getSerializableExt
47、ra(HEAP_DUMP_EXTRA); AnalysisResult result = (AnalysisResult) intent.getSerializableExtra(RESULT_EXTRA); try / 可以在onHeapAnalyzed()方法中上报内存泄露等操作 onHeapAnalyzed(heapDump, result); finally /noinspection ResultOfMethodCallIgnored / 最后把该内存映射文件删除掉,该轮分析也就结束了 heapDump.heapDumpFile.delete(); /* * Called after a heap dump is analyzed, whether or not a leak was found. * Check link AnalysisResult#leakFound and link AnalysisResult#excludedLeak to see if there * was a leak and if it can be ignored. * * This will be called from a background intent service thread. * *
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 临床试验合作协议的范例解析
- 商家联盟合作契约范本
- 2024土地权属纠纷调解案例
- 医疗器械注册委托代理合同2024年
- 公司与学生双方实习协议书范本
- 建筑公司劳务合同书范本格式
- 标准超市租赁合同范本
- 工厂物资采购销售合同范本
- 《年度汽车销售合作协议》主体变更协议
- 昆明劳动合同范本
- 幼儿园园长的幼教教研与项目管理
- 健康评估练习题大全(含答案)
- 新北师大版小学数学二年级上册《六-测量:课桌有多长》-公开课教案-1
- 构建文明校园共同创造和谐学园
- 铁路边坡水害分析报告
- 南平出租车从业资格证模拟考试题
- 《怎样听课评课》课件
- 建筑施工工程投入的主要施工机械设备情况描述及进场计划
- 访问学者管理制度
- D报告样板设备故障的8D报告
- 2023超星尔雅舞蹈鉴赏章节测试考试答案
评论
0/150
提交评论