说到内存泄漏,就必须提到LeakCanary. 这个利器,很方便的显示出内存泄漏的地方。在用到的过程中好奇怎么做到的,下面跟着网上的博客简单的分析下LeakCanary源码。
源码分析
1. 注册
使用很简单,关键的一行代码“LeakCanary.install(this)”;下面就以这行代码为线索分析。
构建一个AndroidRefWatcherBuilder的对象。
而AndroidRefWatcherBuilder继承自RefWatcherBuilder。
创建的实际是子类对象,因此后续调用的都是子类的方法,除非子类不存在,则会调用父类的方法。
而传进去的参数DisplayService.这个类是用于内存泄漏后的展示。
这个是创建那些需要去除的引用,因为有些内存泄漏是已知,且是系统层级的。因此判断是否内存泄漏的时候需要排除。这个也可以手动增加,比如这个类的泄漏不严重,但改起来代价太大,就可以手动将这个类放进去。
最后调用这个方法,关键的地方在于build()这个方法,通过建造者设计模式设置参数。因为前面创建的是子类对象,因此调用方法时会优先调用子类的方法。另外值得注意的是最后new RefWatcher时,会去创建一个Disabled的父类的对象,也会去调用build()方法。
enableDisplayLeakActivity 这个用来控制内存泄漏图标的显示与否。
注册activity的ActivityLifecycleCallbacks. 这样就可以在activity的ondestroy方法中执行内存泄漏的检查。其实可以从此处看出LeakCanary 的缺点,一个是只能检测到activity的内存泄漏,另一个是只能检测非前台的activity的内存泄漏。比如service 的内存泄漏就检测不到。
2 检测
当activity 执行到ondestroy 方法时,会回调ActivityLifecycleCallbacks.
然后开始进行内存泄漏的检查,一路跟进去关键代码如下:
而这个watchExcuter 则是在最开始install 的时候通过builder 方法创建。实际如下
关键代码如下
Retryable 实际上是一个runnable对象。可以看到execute的retryable,最后都会在主进程中执行。
而MessageQueue.IdleHandler可以用来在线程空闲的时候,指定一个操作,使用IdleHandler的好处在于可以不用指定一个将来时间,只要线程空闲了,就可以执行它指定的操作。
这个是界面跳转后内存泄漏的检查需要稍等下的原因之一。另外一个原因可能是生成分析dump文件比较费时。
接下来回头看ensuregone方法
removeWeaklyReachableReferences方法会需要试着移除弱引用,如果移除后的集合中不包括activity的引用对象。则说明没有泄漏,否则就需要手动gc, 然后通过removeWeaklyReachableReferences检查gc后 。如果此时集合中还有activity对应的引用对象,就说明内存泄漏了。否则就不认为发生内存泄漏。如果发生内存泄漏,接下来就需要检测出到底什么地方发生了泄漏。
此时的heapDumper也是最开始install的时候通过build()方法构造的。
这个地方是调用的系统的Debug类的dumpHprofData,来生成dump文件,文件名就是传进去的参数。值得深挖的是Debug这个类,有很多有用的帮助调试的方法。
生成文件之后 然后会走到heapDumpListener。通过analyze 开始正式对dump文件分析。
然后就调到HeapAnalyzerService的runAnalysis方法。HeapAnalyzerService是一个intentservice类,执行结束,这个service会自动销毁。然后runAnalysis调用到onHandleIntent 方法中去。需要注意的是,因为HeapAnalyzerService在manifest中是标记的另外的进程,所以此处虽然是startservice,这样启动service,但启动的service 运行在新的进程中。
HeapAnalyzer会使用checkForLeak的方法来分析内存泄露结果。
这里又个值得注意的地方是,老版本的LeakCanary使用的是eclipse.mat 这个包来分析的。而新版本的则是使用的squaredup的haha库。
此处也是跨进程调用的。然后结果就会传给最开始的displayleakservice。然后就会发通知到通知栏。然后开发者就可以很方便的点进去看到相关的内存泄漏了。
3 总结
一路下来,代码并不是很难,不过多看几遍就会有收获的。有如下知识点:
ActivityLifecyleCallbacks的使用
弱引用及referenceQueu的使用
builder 构造者设计模式
简单的startservice 跨进程通信
MessageQueue.IdleHandler可以用来在线程空闲的时候,指定一个操作
Debug辅助调试类。
引用
https://www.jianshu.com/p/481775d198f0
这篇文章讲的很好,基本上就是自己想说的,截图什么的都是来自这篇文章。感谢分享。
网友评论