美文网首页
2020-08-19 LeakCanary如何运作

2020-08-19 LeakCanary如何运作

作者: 馒Care | 来源:发表于2020-08-19 17:59 被阅读0次

https://square.github.io/leakcanary/fundamentals-how-leakcanary-works/
一旦安装了LeakCanary,它就会通过4个步骤自动检测并报告内存泄漏:

  1. 检测保留的对象。
  2. 转储堆。
  3. 分析堆。
  4. 分类泄漏。

1.检测残留物

LeakCanary可以挂接到Android生命周期中,以自动检测活动和片段何时被破坏并应进行垃圾收集。这些已销毁的对象将传递到ObjectWatcher,其中包含对它们的弱引用。LeakCanary自动检测以下对象的泄漏:

  • 销毁Activity实例
  • 销毁Fragment实例
  • 破坏片段View实例
  • 清除ViewModel实例

您可以观看不再需要的任何对象,例如分离视图或损坏的演示者:

<pre id="__code_0" style="box-sizing: inherit; color: var(--md-code-fg-color); font-feature-settings: "kern"; font-family: "Roboto Mono", SFMono-Regular, Consolas, Menlo, monospace; direction: ltr; position: relative; margin: 1em 0px; line-height: 1.4;">AppWatcher.objectWatcher.watch(myDetachedView, "View was detached") </pre>

如果等待5秒钟并运行垃圾回收ObjectWatcher后未清除由持有的弱引用,则视为被监视的对象已保留,并且有可能泄漏。LeakCanary将此记录到Logcat:

<pre id="__code_1" style="box-sizing: inherit; color: var(--md-code-fg-color); font-feature-settings: "kern"; font-family: "Roboto Mono", SFMono-Regular, Consolas, Menlo, monospace; direction: ltr; position: relative; margin: 1em 0px; line-height: 1.4;">`D LeakCanary: Watching instance of com.example.leakcanary.MainActivity
(Activity received Activity#onDestroy() callback)

... 5 seconds later ...

D LeakCanary: Scheduling check for retained objects because found new object
retained` </pre>

LeakCanary在转储堆之前等待保留对象的数量达到阈值,并显示具有最新数量的通知。

通知

图1. LeakCanary找到了4个保留的对象。

<pre id="__code_2" style="box-sizing: inherit; color: var(--md-code-fg-color); font-feature-settings: "kern"; font-family: "Roboto Mono", SFMono-Regular, Consolas, Menlo, monospace; direction: ltr; position: relative; margin: 1em 0px; line-height: 1.4;">D LeakCanary: Rescheduling check for retained objects in 2000ms because found only 4 retained objects (< 5 while app visible) </pre>

信息

默认阈值5保留的对象时,应用程序是可见的,和1保留对象时,应用程序是不可见的。如果您看到保留对象的通知,然后将应用程序置于后台(例如,通过按“主页”按钮),则阈值将从5更改为1,LeakCanary会在5秒钟内转储堆。点击通知将强制LeakCanary立即转储堆。

2.转储堆

当保留对象的数量达到阈值时,LeakCanary将Java堆转储到存储在Android文件系统上的.hprof文件(堆转储)中(请参阅LeakCanary在何处存储堆转储?)。转储堆会使应用程序冻结一小段时间,在此期间LeakCanary会显示以下吐司:

吐司

图2. LeakCanary显示了在堆放时的吐司

3.分析堆

LeakCanary .hprof使用Shark解析文件,并在该堆转储中找到保留的对象。

做完了

图3. LeakCanary在堆转储中查找保留的对象。

对于每个保留的对象,LeakCanary会查找引用路径,以防止对该保留的对象进行垃圾回收:其泄漏跟踪。您将在下一部分中学习分析泄漏跟踪:修复内存泄漏

做完了

图4. LeakCanary计算每个保留对象的泄漏跟踪。

分析完成后,LeakCanary将显示带有摘要的通知,并在Logcat中打印结果。请注意下面的4个保留对象如何被分组为2个不同的泄漏。LeakCanary 为每个泄漏跟踪创建一个签名,并将具有相同签名的泄漏(即,由同一bug引起的泄漏)组合在一起。

做完了

图5. 4条泄漏迹线变成2个不同的泄漏特征。

<pre id="__code_3" style="box-sizing: inherit; color: var(--md-code-fg-color); font-feature-settings: "kern"; font-family: "Roboto Mono", SFMono-Regular, Consolas, Menlo, monospace; direction: ltr; position: relative; margin: 1em 0px; line-height: 1.4;">`====================================
HEAP ANALYSIS RESULT
====================================
2 APPLICATION LEAKS

Displaying only 1 leak trace out of 2 with the same signature
Signature: ce9dee3a1feb859fd3b3a9ff51e3ddfd8efbc6
┬───
│ GC Root: Local variable in native code

...` </pre>

轻触通知将启动提供更多详细信息的活动。稍后点击LeakCanary启动器图标再次回到它:

吐司

图6. LeakCanary为安装的每个应用程序添加一个启动器图标。

每行对应一组具有相同签名的泄漏。LeakCanary 在应用程序首次使用该签名触发泄漏时将行标记为“ 新建 ”。

吐司

图7.将4个泄漏分组为2行,每个泄漏标记对应一个。

点击泄漏以打开带有泄漏轨迹的屏幕。您可以通过下拉菜单在保留的对象及其泄漏跟踪之间切换。

吐司

图8.屏幕显示了3个按其常见泄漏特征分组的泄漏。

泄漏签名每个级联的散列参考怀疑导致泄漏,即,每个参考与红色下划线显示

吐司

图9.带有3个可疑参考的泄漏跟踪。

~~~当泄漏跟踪以文本形式共享时,这些相同的可疑引用都带有下划线:

<pre id="__code_4" style="box-sizing: inherit; color: var(--md-code-fg-color); font-feature-settings: "kern"; font-family: "Roboto Mono", SFMono-Regular, Consolas, Menlo, monospace; direction: ltr; position: relative; margin: 1em 0px; line-height: 1.4;">... │ ├─ com.example.leakcanary.LeakingSingleton class │ Leaking: NO (a class is never leaking) │ ↓ static LeakingSingleton.leakedViews │ ~~~~~~~~~~~ ├─ java.util.ArrayList instance │ Leaking: UNKNOWN │ ↓ ArrayList.elementData │ ~~~~~~~~~~~ ├─ java.lang.Object[] array │ Leaking: UNKNOWN │ ↓ Object[].[0] │ ~~~ ├─ android.widget.TextView instance │ Leaking: YES (View.mContext references a destroyed activity) ... </pre>

在上面的示例中,泄漏的签名将计算为:

<pre id="__code_5" style="box-sizing: inherit; color: var(--md-code-fg-color); font-feature-settings: "kern"; font-family: "Roboto Mono", SFMono-Regular, Consolas, Menlo, monospace; direction: ltr; position: relative; margin: 1em 0px; line-height: 1.4;">val leakSignature = sha1Hash( "com.example.leakcanary.LeakingSingleton.leakedView" + "java.util.ArrayList.elementData" + "java.lang.Object[].[x]" ) println(leakSignature) // dbfa277d7e5624792e8b60bc950cd164190a11aa </pre>

4.对泄漏进行分类

LeakCanary将在应用程序中发现的泄漏分为两类:应用程序泄漏库泄漏。一个库泄漏是由3个已知的bug泄漏RD党的代码,你没有控制权。此泄漏正在影响您的应用程序,但是很遗憾,修复泄漏可能不受您的控制,因此LeakCanary会将其分离出来。

Logcat中打印的结果中将这两类分开:

<pre id="__code_6" style="box-sizing: inherit; color: var(--md-code-fg-color); font-feature-settings: "kern"; font-family: "Roboto Mono", SFMono-Regular, Consolas, Menlo, monospace; direction: ltr; position: relative; margin: 1em 0px; line-height: 1.4;">`====================================
HEAP ANALYSIS RESULT
====================================
0 APPLICATION LEAKS

====================================
1 LIBRARY LEAK

...
┬───
│ GC Root: Local variable in native code

...` </pre>

LeakCanary 在其泄漏列表中将一行标记为库泄漏:

图书馆泄漏

图10. LeakCanary发现了Library Leak。

LeakCanary附带了一个已知泄漏的数据库,它可以通过对引用名称进行模式匹配来识别。例如:

<pre id="__code_7" style="box-sizing: inherit; color: var(--md-code-fg-color); font-feature-settings: "kern"; font-family: "Roboto Mono", SFMono-Regular, Consolas, Menlo, monospace; direction: ltr; position: relative; margin: 1em 0px; line-height: 1.4;">Leak pattern: instance field android.app.Activity$1#this$0 Description: Android Q added a new IRequestFinishCallback$Stub class [...] ┬─── │ GC Root: Global variable in native code │ ├─ android.app.Activity$1 instance │ Leaking: UNKNOWN │ Anonymous subclass of android.app.IRequestFinishCallback$Stub │ ↓ Activity$1.this$0 │ ~~~~~~ ╰→ com.example.MainActivity instance </pre>

我做了什么导致这种泄漏?

没有什么不对!您按预期的方式使用了API,但是实现中存在一个导致此泄漏的错误。

有什么我可以防止的吗?

也许!可以使用反射来修复某些库泄漏,而其他一些泄漏则可以通过执行使泄漏消失的代码路径来解决。这种类型的修复程序往往很容易破解,所以要当心!最好的选择是查找错误报告或提交错误报告,并坚持认为该错误已得到修复。

由于我不能对此泄漏做太多事情,有没有办法让LeakCanary忽略它?

LeakCanary无法在转储并分析堆之前知道泄漏是否是库泄漏。如果在找到“库泄漏”时LeakCanary没有显示结果通知,那么您将开始怀疑在倾倒烤面包后LeakCanary分析发生了什么。

您可以在AndroidReferenceMatchers类中查看已知泄漏的完整列表。如果发现无法识别的Android SDK泄漏,请报告。您还可以自定义已知库泄漏的列表

相关文章

网友评论

      本文标题:2020-08-19 LeakCanary如何运作

      本文链接:https://www.haomeiwen.com/subject/ibjdjktx.html