Leakcanary 检查OOM

本文介绍LeakCanary的原理及集成步骤,包括如何监控Activity和Fragment的内存泄漏,自定义UI样式,导出hprof文件,上传leaktrace到服务器等高级功能。

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

内存泄漏是困扰开发者的一大问题,上篇讲述了如何用MAT分析hprof文件来优化内存,那么今天来介绍一下Leakcanary这个更智能的好东东:


Leakcanary的原理

  1. RefWatcher.watch() 创建一个 KeyedWeakReference 到要被监控的对象。

  2. 然后在 AndroidWatchExecutor 的后台线程检查引用是否被清除,如果没有,调用GC。

  3. 如果引用还是未被清除,把 heap 内存 dump 到 APP 对应的文件系统中的一个 .hprof 文件中。

  4. 在另外一个进程中的 HeapAnalyzerService 有一个 HeapAnalyzer 使用HAHA 解析这个文件。

  5. 得益于唯一的 reference key, HeapAnalyzer 找到 KeyedWeakReference,定位内存泄露。

  6. HeapAnalyzer 计算 到 GC roots 的最短强引用路径,并确定是否是泄露。如果是的话,建立导致泄露的引用链。

  7. 内存泄漏信息送回给 DisplayLeakService,它是运行在 app 进程里的一个服务。然后通过一个独立的页面显示出来

    <activity
        android:name="com.squareup.leakcanary.internal.DisplayLeakActivity"
        android:enabled="false"
        android:icon="@drawable/leak_canary_icon"
        android:label="@string/leak_canary_display_activity_label"
        android:taskAffinity="com.squareup.leakcanary"
        android:theme="@style/leak_canary_LeakCanary.Base" >
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />
    
            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>


集成步骤


首先引入jar包

dependencies {
    debugCompile 'com.squareup.leakcanary:leakcanary-android:1.4-beta2'
    releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.4-beta2'
}

自定义Appliacation并配置

private static RefWatcher refWatcher;

public static RefWatcher getRefWatcher(Context context) { return refWatcher;}

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

LeakCanary 自动检测 Activity 泄漏只支持 Android ICS 以上版本。因为 Application.registerActivityLifecycleCallbacks() 是在 API 14 引入的。如果要在 ICS 之前监测 Activity 泄漏,可以重载 Activity.onDestroy() 方法,然后在这个方法里调用 RefWatcher.watch(this) 来实现。

注意:LeakCanary 自动检测 Activity 泄漏只支持 Android ICS 以上版本。因为 Application.registerActivityLifecycleCallbacks() 是在 API 14 引入的。如果要在 ICS 之前监测 Activity 泄漏,可以重载 Activity.onDestroy() 方法,然后在这个方法里调用 RefWatcher.watch(this) 来实现。


使用 RefWatcher 监控 Fragment:


  
public class BaseFragment extends Fragment {

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


监控其他泄漏
RefWatcher refWatcher = ExampleApplication.getRefWatcher(getActivity());
refWatcher.watch(someObjNeedGced);

当 someObjNeedGced 还在内存中时,就会在 logcat 里看到内存泄漏的提示。


自定义UI样式



res/
  drawable-hdpi/
    __leak_canary_icon.png
  drawable-mdpi/
    __leak_canary_icon.png
  drawable-xhdpi/
    __leak_canary_icon.png
  drawable-xxhdpi/
    __leak_canary_icon.png
  drawable-xxxhdpi/
    __leak_canary_icon.png

<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="__leak_canary_display_activity_label">MyLeaks</string>
</resources>

保存 leak trace

在 APP 的目录中,DisplayLeakActivity 保存了 7 个 dump 文件和 leak trace。你可以在你的 APP 中,定义R.integer.__leak_canary_max_stored_leaks 来覆盖类库的默认值。

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

如何导出 hprof 文件

File heapDumpFile = new File("heapdump.hprof");
Debug.dumpHprofData(heapDumpFile.getAbsolutePath());



可以参阅 AndroidHeapDumper.java 的代码。


上传 leak trace 到服务器

你可以改变处理完成的默认行为,将 leak trace 和 heap dump 上传到你的服务器以便统计分析。

创建一个 LeakUploadService, 最简单的就是继承 DisplayLeakService

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);
   }
}


请确认 release 版本 使用 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);
   }
}

别忘了注册 service:

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


有时,leak trace 不够,你需要通过 MAT深挖 dump 文件。

通过以下方法,你能找到问题所在:

  1. 查找所有的 com.squareup.leakcanary.KeyedWeakReference 实例。
  2. 检查 key 字段
  3. Find the KeyedWeakReference that has a key field equal to the reference key reported by LeakCanary.
  4. 找到 key 和 和 logcat 输出的 key 值一样的 KeyedWeakReference
  5. referent 字段对应的就是泄露的对象。
  6. 剩下的,就是动手修复了。最好是检查到 GC root 的最短强引用路径开始。


更多leakcanary相关  请移步:https://github.com/square/leakcanary/wiki/FAQ



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值