<pre style="margin: 8px 0px;">LeakCananry介绍: </pre>
LeakCananry是开源大户Square的一款开源产品,用于检测程序中的内存泄露
目前为止最新的版本是2.3版本,相比于2.0之前的版本,2.0之后的版本在使用上简洁了很多,只需要在dependencies中加入LeakCanary的依赖即可。而且debugImplementation只在debug模式下有效,所以不用担心用户在正式环境下也会出现LeakCanary收集。
LeakCanray 2.0为啥不需要在application里调install?
LeakCanary1.x 是基于 Java,LeakCanary2.x 是基于 Kotlin ,背后原理是类似的;
****如何安装的,自己是一个cotentprovider
这个什么时候初始化,activityThread 的作用
初始化
在 1.x 中需要在
Application#onCreate
中显式初始化;在 2.x 中不需要,是因为初始化放在
ContentProvider
中ContentProvider 在创建 Application 之前加载,LeakCanary 在 ContentProvider 中初始化;
有什么影响:启动速度慢。性能开销很大。只有开发的时候才用到。****
新版感觉不好用,引用链看不懂
它不会立刻内存泄露,要等一会儿。
3. ****解决内存泄漏的工具LeakCanary:
leakcanary存放hprof在哪
调用heapDumper.dumpHeap()生成.hprof文件目录
****一句话总结:分析.hprof文件和MAT一样****
缺点
1.LeakCanary的问题
:LeakCanary也有一定的不确定性,一般同一个地方反复泄漏5次,算是一个泄漏,同时不建议用在线上环境。
2.LeakCanary 基于 Shark 分析引擎分析,分析速度较慢,通常在 5 分钟以上才能分析完成,分析过程会影响进程内存占用。
3.LeakCanary查找出来的是可能性有问题的。
** 4.****leakcanry尽管使用了idlehandler与分进程,但是dumphprof依然会造成应用明显的卡顿(SuspendAll Thread)。而在三星等一些手机,系统会缓存最后一个Activity,所以在微信,我们采取了更严格的检测模式,即泄露三次确认以及经过5个新建的Activity,确保不是由于系统缓存的原因造成。**
**内存泄露判断与处理的流程如下图 ,各自运行的进程空间(主进程通过idlehandler,HAHA分析使用的是单独的进程)******:****
leakcanary 原理://www.greatytc.com/p/5ee6b471970e
比较新的分析:
https://blog.csdn.net/chennai1101/article/details/103799424
LeakCanary原理总结:
- 初始化:直接debugImplementation就能实现,他是在 ContentProvider里面做的初始化,当打包的时候,会合并各个清单文件。里面注册的 ContentProvider,ContentProvider 会在 Application的 attachBaseContext 之后, onCreate之前创建。在ContentProvider的出事时候,
2. 在Application里面注册监控所以activitity的生命周期,注册 Application.ActivityLifecycleCallbacks 监听Activity的生命周期,以及 fragmentManager.registerFragmentLifecycleCallbacks监听Fragment的生命周期
3.ondestroy 调用RefWatcher的watch(),比如监听 onActivityDestroyed方法,当监听到这个方法调用的时候,把Activity 全部放到观察数组中,并且用引用队列包裹这个activity,生成key(UUID),然后过5s,看看引用队列里面有没有这个key,如果有,证明回收了,然后把观察数组中的remove掉这个key,此时如果这个数组里面的count > 0 ,证明有可能是怀疑的泄漏,然后 调用 Runtime.getRuntime().gc(),之后再 看看 引用队列有没有这个数据,如果有,然后把观察数组中的remove掉这个key,之后再看观察数组中的count,如果小于5,只是提示一下。如果count 大于 5 (防止卡顿),就开始使用 shark (2.0之前是haha)分析堆栈信息。用可达到性分析,找到最短的链路,
4.**然后在后台线程检查引用是否被清除,如果没有,调用GC。 **
5.****如果引用还是未被清除,dump ,然后生成一个.hprof文件
6.开一个服务和线程去分析和解析这个文件
6.计算 到 GC roots 的最短强引用路径(在ui会显示),并确定是否是泄露。如果是的话,建立导致泄露的引用链。
原理
:
1.LeakCanary通过ApplicationContext统一注册监听的方式,通过application.registerActivityLifecycleCallbacks来绑定Activity生命周期的监听,从而监控所有Activity;
2.在Activity执行onDestroy时,开始检测当前页面是否存在内存泄漏,并分析结果。
3.KeyedWeakReference与ReferenceQueue联合使用,在弱引用关联的对象被回收后,会将引用添加到ReferenceQueue;清空后,可以根据是否继续含有该引用来判定是否被回收;判定回收,
4.手动GC, 再次判定回收,采用双重判定来确保当前引用是否被回收的状态正确性;如果两次都未回收,则确定为泄漏对象。
调用步骤 :
在ondestory();
1、 首先通过removeWeaklyReachablereference来移除已经被回收的Activity引用
2、 通过gone(reference)判断当前弱引用对应的Activity是否已经被回收,如果已经回收说明activity能够被GC,直接返回即可。
3、 如果Activity没有被回收,调用GcTigger.runGc方法运行GC,手动GC完成后在运行第1步,然后运行第2步判断Activity是否被回收了,如果这时候还没有被回收,那就说明Activity可能已经泄露。
4、 如果Activity泄露了,就抓取内存dump文件
5、 之后通过HeapAnalyzerService.runAnalysis进行分析内存文件分析 ,分析profier文件.得到引用链
我自己的总结:
1.contentprovider启动
2.注册生命周期,在activity和fragment结束的时候会进行检测
3.引用队列RefrenceQueue,5s有没有被清楚。手动调用GC,Runtime
4.找到有问题的引用,开始分析(可达分析),是否是真正的内存泄露
这个工具可以查看内存快照,就是hprofiy文件
手写Leakcanary工具的核心代码。
ReferenceQueue referenceQueue = new ReferenceQueue<Object>();
Object object = new Object();
WeakReference<Object> weakReference = new WeakReference<Object>(object, referenceQueue);
Log.d("MyReference", "weakReference" + weakReference);
object = null;
Runtime.getRuntime().gc();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
//从队列中取弱引用,如果不为空,说明有回收了,手动让object=null;
//如果为空,说明没有什么可以回收的
WeakReference<Object> weakReference1 = (WeakReference) referenceQueue.poll();
Log.d("MyReference", "after weakReference1" + weakReference1);
Object object1 = null;
if (referenceQueue.poll() != null) {
object1 = referenceQueue.poll().get();
}
Log.d("MyReference", "after object1" + object1);
打印:
2021-03-14 12:29:15.410 29008-29008/com.example.myapplication.myapplication D/MyReference: after weakReference1java.lang.ref.WeakReference@6146678
2021-03-14 12:29:15.410 29008-29008/com.example.myapplication.myapplication D/MyReference: after object1null
不把object置空,不会回收
2021-03-14 12:33:46.753 29009-29009/com.example.myapplication.myapplication D/MyReference: after weakReference1null
2021-03-14 12:33:46.753 29009-29009/com.example.myapplication.myapplication D/MyReference: after object1null
结论: 引用队列获取弱引用,如果是弱引用存在说明回收了,如果当GC过后引用对象仍然不被加入ReferenceQueue中,不存在说明内存泄漏了。 存在说明没有泄漏
核心原理:引用队列可以配合软引用、弱引用及虚引用使用,引用的对象将要被JVM回收时,会将其加入到引用队列中。
监测机制利用了Java的WeakReference和ReferenceQueue,弱引用和引用队列,!
在构造时我们需要传入一个ReferenceQueue,这个ReferenceQueue是直接传入了WeakReference中
通过将Activity包装到WeakReference中,被WeakReference包装过的Activity对象如果被回收,该WeakReference引用会被放到ReferenceQueue中,通过监测ReferenceQueue里面的内容就能检查到Activity是否能够被回收
Android 引用队列
核心原理:弱引用存放对象,当gc回收时候,ReferenceQueue存放引用,就是weak
引用队列可以配合软引用、弱引用及幽灵引用使用,当引用的对象将要被JVM回收时,会将其加入到引用队列中,LeakCanary就是用的这个原理来检测Activity或者Fragment是否有泄漏
Android 查看对象回收ReferenceQueue和内存泄漏?
WeakReference referencequequ
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;
private final ExcludedRefs excludedRefs;
- 说一说Android中如何查看一个对象的回收情况 ?(字节跳动)
[图片上传失败...(image-92b695-1625703347881)]
首先要了解Java 四种引用类型的场景和使用(强引用、软引用、弱引用、虛引用)
o 举个场景例子:SoftReference 对象是用来保存软引用的,但它同时也是一个Java 对象,所以当软引用对象被回
收之后,虽然这个SoftReference 对象的get 方法返回null,但SoftReference 对象本身并不是null,而此时这个SoftReference 对象已经不再具有存在的价值,需要一
个适当的清除机制,避免大量SoftReference 对象带来的内存泄露o 因此,Java 提供ReferenceQueue 来处理引用对象的回收
情况。当SoftReference 所引用的对象被GC 后,JVM 会先将softReference 对象添加到ReferenceQueue 这个队列
中。当我们调用ReferenceQueue 的poll()方法,如果这个队列中不是空队列,那么将返回并移除前面添加的那个Reference 对
垃圾回收机制:
Android虚拟机的垃圾回收采用的是根搜索算法。GC会从根节点(GC Roots)开始对heap进行遍历。到最后,部分没有直接或者间接引用到GC Roots的就是需要回收的垃圾,会被GC回收掉。但是当对象不再被应用程序使用,仍然被生命周期长的对象引用,垃圾回收器无法回收。