LeakCanary: 让内存泄露无所遁形

09 May 2015


本文为LeakCanary: Detect all memory leaks!的翻译。原文在: https://corner.squareup.com/2015/05/leak-canary.html

java.lang.OutOfMemoryError
at android.graphics.Bitmap.nativeCreate(Bitmap.java:-2)
at android.graphics.Bitmap.createBitmap(Bitmap.java:689)
at com.squareup.ui.SignView.createSignatureBitmap(SignView.java:121)

谁也不会喜欢 OutOfMemoryError

Square Register 中, 在签名页面,我们把客户的签名画在 bitmap cache 上。 这个 bitmap 的尺寸几乎和屏幕的尺寸一样大,在创建这个 bitmap 对象时,经常会引发 OutOfMemoryError,简称OOM

当时,我们尝试过一些解决方案,但都没解决问题

  • 使用 Bitmap.Config.ALPHA_8 因为,签名仅有黑色。

  • 捕捉 OutOfMemoryError, 尝试 GC 并重试(受 GCUtils 启发)。

  • 我们没想过在 Java heap 内存之外创建 bitmap 。苦逼的我们,那会 Fresco 还不存在。

路子走错了

其实 bitmap 的尺寸不是真正的问题,当内存吃紧的时候,到处都有可能引发 OO。在创建大对象,比如 bitmap 的时候,更有可能发生。OOM 只是一个表象,更深层次的问题可能是: 内存泄露

什么是内存泄露

一些对象有着有限的生命周期。当这些对象所要做的事情完成了,我们希望他们会被回收掉。但是如果有一系列对这个对象的引用,那么在我们期待这个对象生命周期结束的时候被收回的时候,它是不会被回收的。它还会占用内存,这就造成了内存泄露。持续累加,内存很快被耗尽。

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

对战内存泄露

排查内存泄露是一个全手工的过程,这在 Raizlabs 的 Wrangling Dalvik 系列文章中有详细描述。

以下几个关键步骤:

  1. 通过 Bugsnag, Crashlytics 或者 Developer Console 等统计平台,了解 OutOfMemoryError 情况。

  2. 重现问题。为了重现问题,机型非常重要,因为一些问题只在特定的设备上会出现。为了找到特定的机型,你需要想尽一切办法,你可能需要去买,去借,甚至去偷。 当然,为了确定复现步骤,你需要一遍一遍地去尝试。一切都是非常原始和粗暴的。

  3. 在发生内存泄露的时候,把内存 Dump 出来。具体看这里

  4. 然后,你需要在 MAT 或者 YourKit 之类的内存分析工具中反复查看,找到那些原本该被回收掉的对象。

  5. 计算这个对象到 GC roots 的最短强引用路径。

  6. 确定引用路径中的哪个引用是不该有的,然后修复问题。

很复杂对吧?

如果有一个类库能在发生 OOM 之前把这些事情全部都搞定,然后你只要修复这些问题就好了,岂不妙哉!

LeakCanary

LeakCanary 是一个检测内存泄露的开源类库。你可以在 debug 包种轻松检测内存泄露。

先看一个例子:

class Cat {
} class Box {
Cat hiddenCat;
}
class Docker {
// 静态变量,将不会被回收,除非加载 Docker 类的 ClassLoader 被回收。
static Box container;
} // ... Box box = new Box(); // 薛定谔之猫
Cat schrodingerCat = new Cat();
box.hiddenCat = schrodingerCat;
Docker.container = box;

创建一个RefWatcher,监控对象引用情况。

// 我们期待薛定谔之猫很快就会消失(或者不消失),我们监控一下
refWatcher.watch(schrodingerCat);

当发现有内存泄露的时候,你会看到一个很漂亮的 leak trace 报告:

  • GC ROOT static Docker.container
  • references Box.hiddenCat
  • leaks Cat instance

我们知道,你很忙,每天都有一大堆需求。所以我们把这个事情弄得很简单,你只需要添加一行代码就行了。然后 LeakCanary 就会自动侦测 activity 的内存泄露了。

public class ExampleApplication extends Application {
@Override public void onCreate() {
super.onCreate();
LeakCanary.install(this);
}
}

然后你会在通知栏看到这样很漂亮的一个界面:

结论

使用 LeakCanary 之后,我们修复了我们 APP 中相当多的内存泄露。我们甚至发现了 Android SDK 中的一些内存泄露问题

结果是惊艳的,我们减少了 94% 的由 OOM 导致的 crash。

如果你也想消灭 OOM crash,那还犹豫什么,赶快使用 LeakCanary

相关链接:

LeakCanary: 让内存泄露无所遁形的更多相关文章

  1. 使用LeakCanary检测内存泄露 翻译 MD

    Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱 MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina ...

  2. Android内存优化8 内存检测工具2 LeakCanary——直白的展现Android中的内存泄露

    之前碰到的OOM问题,终于很直白的呈现在我的眼前:我尝试了MAT,但是发现不怎么会用.直到今天终于发现了这个新工具: 当我们的App中存在内存泄露时会在通知栏弹出通知: 当点击该通知时,会跳转到具体的 ...

  3. LeakCanary——直白的展现Android中的内存泄露

    之前碰到的OOM问题,终于很直白的呈现在我的眼前:我尝试了MAT,但是发现不怎么会用.直到今天终于发现了这个新工具: 当我们的App中存在内存泄露时会在通知栏弹出通知: 当点击该通知时,会跳转到具体的 ...

  4. Android性能优化之利用LeakCanary检测内存泄漏及解决办法

    前言: 最近公司C轮融资成功了,移动团队准备扩大一下,需要招聘Android开发工程师,陆陆续续面试了几位Android应聘者,面试过程中聊到性能优化中如何避免内存泄漏问题时,很少有人全面的回答上来. ...

  5. JVM内存管理概述与android内存泄露分析

    一.内存划分 将内存划分为六大部分,分别是PC寄存器.JAVA虚拟机栈.JAVA堆.方法区.运行时常量池以及本地方法栈. 1.PC寄存器(线程独有):全称是程序计数寄存器,它记载着每一个线程当前运行的 ...

  6. [转]深入Android内存泄露

    深入内存泄露 Android应用的内存泄露,其实就是java虚拟机的堆内存泄漏. 当然,当应用有ndk,jni时,没有及时free,本地堆也会出现内存泄漏. 本文只是针对JVM内存泄漏应用,进行阐述分 ...

  7. LeakCanary Android 和 Java 内存泄露检测。

    开始使用 在 build.gradle 中加入引用,不同的编译使用不同的引用: dependencies { debugCompile 'com.squareup.leakcanary:leakcan ...

  8. 内存测试——内存泄露工具 LeakCanary

    项目地址 https://github.com/square/leakcanary 接入方法 1. 配置依赖 dependencies { debugCompile 'com.squareup.lea ...

  9. LeakCanary Android 和 Java 内存泄露检测

    说起内存泄漏还是挺让人头疼的,而且不是每个手机都会发生的情况,往往又不易察觉,那么今天我们就来介绍下LeakCanary这个工具 githup:https://github.com/square/le ...

随机推荐

  1. ios开发学习笔记001-C语言基础知识

    先来学习一下C语言基础知识,总结如下: 在xcode下编写代码. 1.编写代码 2.编译:cc –c 文件名.c 编译成功会生成一个 .o的目标文件 3.链接:把目标文件.o和系统自带的库合并在一起, ...

  2. Java的移位运算符

    1.左移运算符:<< 丢弃左边指定位数,右边补0. 注意: 当int类型进行左移操作时,左移位数大于等于32位操作时,会先求余(%)后再进行左移操作.也就是说左移32位相当于不进行移位操作 ...

  3. vlc无法播放.flv视频文件

    解决方法:https://videoconverter.wondershare.com/vlc/flv-not-displaying-video-vlc-media-player.html. 在pre ...

  4. valuestack 根对象CompoundRoot 源码

    /* * Copyright 2002-2006,2009 The Apache Software Foundation. * * Licensed under the Apache License, ...

  5. 【bzoj3329】Xorequ 数位dp+矩阵乘法

    题目描述 输入 第一行一个正整数,表示数据组数据 ,接下来T行每行一个正整数N 输出 2*T行第2*i-1行表示第i个数据中问题一的解, 第2*i行表示第i个数据中问题二的解, 样例输入 1 1 样例 ...

  6. 【bzoj4439】[Swerc2015]Landscaping 网络流最小割

    题目描述 FJ有一块N*M的矩形田地,有两种地形高地(用‘#’表示)和低地(用‘.’表示) FJ需要对每一行田地从左到右完整开收割机走到头,再对每一列从上到下完整走到头,如下图所示 对于一个4*4的田 ...

  7. Socket通信——服务器和客户端相互通信

    所谓socket通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄.应用程序通常通过"套接字"向网络发出请求或者应答网络请求.  Socket和S ...

  8. Python基础教程笔记 第一章

    /  表示整除,当导入_future_模块中的version时,/ 表示正常的的除法, 此时可用//表示整除,不论数字是整型还是浮点型,都可以用//表示整除. ** 表示幂次方  例如 2**3    ...

  9. get****Context各个方法分析

    1 getApplicationContext分析 该方法为contextImpl类的方法,返回一个ApplicationContext.方法代码为: public Context getApplic ...

  10. VIJOS【1234】口袋的天空

    背景 小杉坐在教室里,透过口袋一样的窗户看口袋一样的天空. 有很多云飘在那里,看起来很漂亮,小杉想摘下那样美的几朵云,做成棉花糖. 描述 给你云朵的个数N,再给你M个关系,表示哪些云朵可以连在一起. ...