[性能优化]使用LeakCanary优化你的app

前言

在日常开发中,可能经常会遇到一些莫名奇妙的崩溃问题,但是仔细查看代码逻辑却似乎也找不出代码中有哪些不对的逻辑。这时候就需要仔细分析,你的代码中是否存在内存泄漏的问题。LeakCanary是Square公司开源的一款性能优化工具,它能够帮你方便的分析你的app中是否存在内存泄漏的问题。在使用LeakCanary之前,让我们先来了解几个概念。

一些概念

  • Java虚拟机
    相信学过Android的你对Java虚拟机一定不会陌生,但还是简单的介绍一下Java虚拟机。如果你学过C/C++,一些C/C++书中都会强调你需要手动分配内存,并在使用之后手动回收内存。但是好像Java中从来没有人让你释放内存啊,这一切都需要归功于Java虚拟机,Java虚拟机能够帮我们自动释放可回收内存。当然,java虚拟机除了能够帮助我们自动回收内存之外,还有很多别的功能,但本文的重点在于内存泄漏,而安卓使用的虚拟机与Java虚拟机相似。所以下面我们聊一聊Java虚拟机的垃圾回收吧。

  • GC
    GC全称(Garbage Collection),也就是垃圾回收,Java虚拟机主要通过两种途径自动帮我们回收内存。

    • 引用计数
      Java虚拟机会给对象增加一个引用计数器,每当程序引用一次对象,计数器就会加一;反之,每当一个引用计数器失效时,计数器就会减一。当计数器的值为0,则说明此对象没有被引用,可以被回收。
      举个例子
      Object obj = new Object(); // 计数器 + 1 = 1
      obj = null; // 计数器 - 1 = 0,GC回收
      // 但是如果对象相互调用,引用计数器就无法使得GC回收
      Object a = new Object(); // a的引用计数为1 
      Object b = new Object(); // b的引用计数为1 
      a.next = b; // a的引用计数为2 
      b.next = a; // b的引用计数为2 
      a = null; // a的引用计数为1,尽管已经显示地将a赋值为null,但是由于引用计数为1,GC无法回收a
      b = null; // b的引用计数为1,同理,GC也不回收b
      

    为了解决对象之间相互引用导致的无法GC的问题,Java虚拟机还有另一种GC策略。

    • 可达性分析
      设立若干根对象(GC Root) ,每个对象都是一个子节点,当一个对象找不到根节点,也就是无人引用时,标志其不可达。
      可以作为GC Root的对象包括:
      1.jvm栈中引用的对象
      2.方法区中静态变量引用的对象
      3.方法区中常量引用的对象
      4.本地方法栈中引用的对象
      5.新生代,活不了多久就死的对象,比如局部变量,用复制算法[1]
      6.老年代,生命周期长的对象,活的久不过也是会死的,用标记清除算法[2]
      7.永久代[3] -----基本上GC不回收

    但即使Java虚拟机已经如此优秀,它也不能保证所有的可回收内存都能正常回收,

  • 内存泄漏
    内存泄漏是指在app运行的过程中,由于内存并没有合理的回收,如:生命周期长的对象持有了生命周期短的对象的引用,导致生命周期短的对象一直无法回收。当这种情况累积到一定程度,可分配的栈内存不足的时候,就会导致OOM,我们就看到了程序崩溃。而且这种崩溃不像一般的程序崩溃那样能够复现,所以直接由程序崩溃,导致了程序员崩溃。
    例如

    • 在onDestroy()调用Android活动实例的方法后,不再需要该活动实例,并且在静态字段中存储对该活动的引用将防止其被垃圾回收。
    • 添加一个Fragment到backstack而没有在Fragment.onDestroyView()中清除它的view的成员。
    • 在一个对象中以成员的方式保存了一个Activity的context,而Activity在配置更改时依然存在。
    • 注册的绑定生命周期对象的监听、广播接收器、RxJava订阅等,在生命周期结束的时候忘记取消注册。
  • OOM
    OOM是(Out Of Memory)的简称,就是内存不足的意思,类似的问题也有StackOverflow,写一个简单的没有出口的递归函数就能看到。

使用LeakCanary

内存泄漏在安卓app中十分常见,小内存泄漏的积累会导致应用内存不足,并导致OOM崩溃。LeakCanary将帮助我们在开发期间找到这些内存泄漏。
使用LeakCanary十分简单,只需要找到·build.gradle·,并在·dependencies·中加入引用即可。

dependencies {
  // debugImplementation because LeakCanary should only run in debug builds.
  debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.1'
}

我们可以通过过滤LeakCanary标签在Logcat中查看。

分析内存泄漏

当发生内存泄漏的时候,LeakCanary会自动帮你保存内存泄漏信息,并将内存泄漏相关的代码以另一个app的形式展示给你,你可以根据提示,修改代码,从而解决内存泄漏的问题。

如何解决内存泄漏呢

内存泄漏常常存在的原因是因为两个或多个对象生命周期不同,同时存在相互引用。导致生命周期短的对象被生命周期长的对象引用后无法正常回收,从而造成内存泄漏。
下面是一个可能经常遇见的内存泄漏的小例子:

  • Activity内存泄漏
    安卓开发中,我们时常会把Activity当做Context传入某些单例如UserInfo等类中,而我们知道,单例的生命周期可能是整个Application的生命周期,远远要比Activity的生命周期要长。如果使用在单例中某些成员变量保存了Activity的引用,当Activity被关闭的时候,就会导致内存泄漏了。所以,当我们写代码的时候,要格外的慎重,如果Context不是必须传入Activity,我们可以将Context传入Application的Context。如果实在非要传入Activity,你可以在使用完Activity只有,将相关的成员变量置空,这个时候,如果发成GC,Activity的引用计数为0,自然就能正常GC了。

这应该是年前写的最后一篇了,希望这篇文章能够帮到你。


  1. 最初是将内存分为相等的两块,只用其中的一块,当这块内存满的时候,将其中存活的对象复制到另一块内存中,然后GC 回收释放之前这块内存。

  2. 就是遍历GC Root,标记可达不可达对象,然后回收不可达对象,这种算法缺点是效率低,无法回收连续物理内存,后来升级为标记 - 整理算法,将可达对象移动到内存的一端,然后GC回收剩下部分连续的物理内存。

  3. 分代算法,在Java中,将内存中的对象按照生命周期长短分成新生代,老年代,永久代

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,558评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,002评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,036评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,024评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,144评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,255评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,295评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,068评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,478评论 1 305
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,789评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,965评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,649评论 4 336
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,267评论 3 318
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,982评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,223评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,800评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,847评论 2 351