LeakCanary,Android内存泄露处理利器

本文介绍了一款名为LeakCanary的内存泄漏检测工具,它适用于Java和Android平台,能有效预防内存泄漏的发生。文中详细讲解了LeakCanary的工作原理、安装配置步骤以及如何通过它来发现和修复内存泄漏。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本篇博文转自黑月神话,原文链接黑月神话
在以前的文章中我讲到过如何使用eclipse和MAT分析内存泄漏(Android内存泄漏分析实战),但是这样的分析往往发生在内存泄漏之后,只能是亡羊补牢。那么我们能不能更早的发现内存泄漏呢?答案是肯定的,LeakCanary能够做到。延伸阅读(LeakCanary源码解析)
这里写图片描述
什么是LeakCanary

    LeakCanary是一个用于检测内存泄漏的工具,可以用于Java和Android,是由著名开源组织Square贡献。

开始使用

debug版本和realse版本用不同的依赖库。由于国内被墙的原因Maven没办法用,导致Android Studio使用不便。所以我自己整合了一个适合eclipse的LeakCanary的库,代码托管在github上面。项目地址:https://github.com/mooncong/Leakcanary-lib.git

dependencies {
   debugCompile 'com.squareup.leakcanary:leakcanary-android:1.3'
   releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.3'
 }
public class ExampleApplication extends Application {

  @Override public void onCreate() {
    super.onCreate();
    LeakCanary.install(this);
  }
}

那么接下来,LeakCanary将会在debug版本中自动探测内存泄漏,当发生内存泄漏的时候就会在通知栏显示一个通知。

什么是内存泄露

对象在其生命周期内完成使命之后,我们就希望这些对象被回收掉。但是如果还存在对象的引用,那么这个对象将不会被回收的。它还会占用内存,这就造成了内存泄露。持续累加,内存很快被耗尽。

比如,当Activity.onDestroy 被调用之后,activity 以及它涉及到的 view 和相关的 bitmap 都应该被回收。但是,如果有一个后台线程持有这个 activity 的引用,那么 activity 对应的内存就不能被回收。这最终将会导致内存耗尽,然后因为 OOM 而 crash。

如何使用

使用RefWatcher监控本应该被垃圾回收器回收的对象

RefWatcher refWatcher = {...};

// 监控一个object对象
refWatcher.watch(object);

LeakCanary.install()返回一个预定义的RefWatcher。它将启动一个ActivityRefWatcher,在Activity.onDestroy()方法调用之后,自动探测Activity的内存泄漏。(注:只支持ICS及以后的版本,具体原因详见代码)

public class ExampleApplication extends Application {

  public static RefWatcher getRefWatcher(Context context) {
    ExampleApplication application = (ExampleApplication) context.getApplicationContext();
    return application.refWatcher;
  }

  private RefWatcher refWatcher;

  @Override public void onCreate() {
    super.onCreate();
    refWatcher = LeakCanary.install(this);
  }
}

也可以使用RefWatcher探测Fragment泄漏

public abstract class BaseFragment extends Fragment {

  @Override public void onDestroy() {
    super.onDestroy();
    RefWatcher refWatcher = ExampleApplication.getRefWatcher(getActivity());
    refWatcher.watch(this);
  }
}

LeakCanary工作原理

RefWatcher.watch()创建一个KeyedWeakReference到北监控的对象。
接下来,在后台线程中检测这个引用是否被清除,如果没有将会触发GC。
如果引用仍然没有清除,将heap内存dump到一个.hprof的文件存放到手机系统里。
HeapAnalyzerService在另外一个独立的进程中启动,使用HeapAnalyzer解析heap内存通过HAHA这个项目
HeapAnalyzer计算出到GC ROOTs的最短强引用路径决定是否发生Leak,然后建立导致泄漏的引用链。
结果被回传到应用程序进程的DisplayLeakService中,然后显示一个泄漏的通知。
如何复制leak trace

在logcat中可以看见leak trace

In com.example.leakcanary:1.0:1 com.example.leakcanary.MainActivity has leaked:
* GC ROOT thread java.lang.Thread.<Java Local> (named 'AsyncTask #1')
* references com.example.leakcanary.MainActivity$3.this$0 (anonymous class extends android.os.AsyncTask)
* leaks com.example.leakcanary.MainActivity instance

* Reference Key: e71f3bf5-d786-4145-8539-584afaecad1d
* Device: Genymotion generic Google Nexus 6 - 5.1.0 - API 22 - 1440x2560 vbox86p
* Android Version: 5.1 API: 22
* Durations: watch=5086ms, gc=110ms, heap dump=435ms, analysis=2086ms

也可以从Action bar分享leak trace和heap内存文件。

如何修复内存泄漏?

一旦发生内存泄漏,找出哪一个引用不应该存在。然后分析为什么它还存在。通常它是一个注册的监听器没有被反注册,或者是close()方法没有调用,一个匿名内部类持有了一个外部类的引用,等等。

Android SDK导致的内存泄漏

在过去的日子里,很多内存泄漏已经被修复了,但是当泄漏发生的时候,普通的应用开发很难去修复它。因此,LeakCanary已经建立了一个已知问题的列表,AndroidExcludedRefs.java。如果你发现一个新问题。请提交一个issue并附上Leak trace,reference key,设备和系统版本。要是附上heap文件的链接就更好了。在新发布的Android版本中尤其重要。你有机会帮助尽早的发现内存泄漏,那将会有益于整个Android社区。

Leak trace之外

有时候Leak trace不能够,可以使用MAT或者YourKit深挖dump文件。MAT使用可以参考以前的一片文章Android内存泄漏分析实战

找到所有的com.squareup.leakcanary.KeyedWeakReference实例。
查看他们的每一个key值。
找到key字段等于LeakCanary报告的引用key的KeyedWeakReference。
KeyedWeakReference的referent字段就是泄漏的对象。
接下来,就是动手修复了。最好是检查到 GC root 的最短强引用路径开始。
保持Leak traces

DisplayLeakActivity默认保存7个heap dumps和leak traces,可以通过下面的配置自定义。

<?xml version="1.0" encoding="utf-8"?>
<resources>
  <integer name="__leak_canary_max_stored_leaks">20</integer>
</resources>

上传 leak trace 到服务器

你可以改变处理完成的默认行为,将 leak trace 和 heap dump 上传到你的服务器以便统计分析。 创建一个 AbstractAnalysisResultService,最简单的就是继承 DefaultAnalysisResultService

public class LeakUploadService extends DisplayLeakService {
  @Override protected void afterDefaultHandling(HeapDump heapDump, AnalysisResult result, String leakInfo) {
    if (!result.leakFound || result.excludedLeak) {
      return;
    }
    myServer.uploadLeakBlocking(heapDump.heapDumpFile, leakInfo);
  }
}

请确认Realse版本的应用使用RefWatcher.DISABLED

public class ExampleApplication extends Application {

  public static RefWatcher getRefWatcher(Context context) {
    ExampleApplication application = (ExampleApplication) context.getApplicationContext();
    return application.refWatcher;
  }

  private RefWatcher refWatcher;

  @Override public void onCreate() {
    super.onCreate();
    refWatcher = installLeakCanary();
  }

  protected RefWatcher installLeakCanary() {
    return RefWatcher.DISABLED;
  }
}

自定义RefWatcher

public class DebugExampleApplication extends ExampleApplication {
  protected RefWatcher installLeakCanary() {
    return LeakCanary.install(app, LeakUploadService.class);
  }
}

不要忘记在manifest中注册Service

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.androi.com/tools"
    >
  <application android:name="com.example.DebugExampleApplication">
    <service android:name="com.example.LeakUploadService" />
  </application>
</manifest>

demo

LeakCanary实战,包含eclipse整合的lib库,以及实例代码。运行代码之后,按照界面提示操作几次,稍等几秒,你将会在通知栏看到一个内存泄漏的通知。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值