文章目录

  • 一、基础
    • 1、添加依赖
    • 2、基本原理
      • 2.1 什么是内存泄漏
      • 2.2 内存泄漏的常见原因
    • 3、为什么要使用LeakCanary
    • 4、LeakCanary是怎么工作的
      • 4.1 Detecting retained objects 检测保留对象
      • 4.2 Dumping the heap 倾倒堆
      • 4.3 Analyzing the heap 堆分析
      • 4.4 Categorizing leaks 泄漏分类
    • 5、修复内存泄漏
      • 5.1 找到泄漏问题
      • 5.2 缩小可疑引用范围
      • 5.3 找到泄漏的引用
      • 5.4 修复泄漏问题
      • 参考:https://square.github.io/leakcanary/

一、基础

1、添加依赖
dependencies {// debugImplementation because LeakCanary should only run in debug builds.debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.7'
}
2、基本原理
2.1 什么是内存泄漏

在基于Java的runtime状态下,内存泄漏是一种变成错误,导致应用保留对无用对象引用,从而引起内存无法回收,导致OOM崩溃。

2.2 内存泄漏的常见原因

大多数内存泄漏时由与生命周期相关的错误引起的,常见Android错误:

  1. Fragment 实例添加到栈中但是没有在onDestoryView()方法中释放
  2. Activity 实例引用作为context字段引用,导致配置变更时activity重建无法释放
  3. 注册广播、监听、或者RxJava添加订阅,未根据生命周期做响应的释放
  4. 数据库查询时cursor使用完毕没有关闭导致
  5. bitmap使用完毕没有置空释放
  6. RecyclerView与NestedScrollView嵌套错误导致无法复用释放资源
3、为什么要使用LeakCanary

内存泄漏在Android应用中非常普遍,小的内存泄漏积累最终会导致OOM异常。LeakCanary可以在开发中帮助发现和修复内存泄漏。

4、LeakCanary是怎么工作的

一旦LeakCanary安装后,会自动检测和报告内存泄漏,主要分为4步:

  1. Detecting retained objects. 检测保留的对象
  2. Dumping the heap. 倾倒堆
  3. Analyzing the heap. 分析堆
  4. Categorizing leaks. 对内存泄漏进行分类
4.1 Detecting retained objects 检测保留对象
  • LeakCanary hook了Android生命周期实现自动检测Activity和Fragment的销毁,并进行垃圾收集。这些被销毁的对象被传递给一个ObjectWatcher,它持有对他们的弱引用,LeakCanary自动检测以下对象的泄漏:
  1. 销毁的Activity实例
  2. 销毁的Fragment实例
  3. 销毁的片段View实例
  4. 清除ViewModel实例

可以通过AppWatcher来对对象的回收过程进行监控

AppWatcher.objectWatcher.watch(myDetachedView, "View was detached")

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

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 objectretained
4.2 Dumping the heap 倾倒堆

当持有的对象到达阀值时,LeakCanary会倾倒Java的堆内存到.hprof文件中,并存储在Android文件系统中。在倾倒堆时会造成App短暂停止,LeakCanary会展示一个如下Toast

4.3 Analyzing the heap 堆分析
  • LeakCanary通过使用Shark库来解析.hprof文件,并定位堆垃圾场中持有的对象

  • 针对每个保留的对象,LeakCanary查询阻止其被垃圾回收的引用路径,进行泄漏跟踪。分析完成后会显示带有摘要的通知,并将结果打印在logcat中。LeakCanary会为每个泄漏trace创建签名,并根据签名对其进行分组

====================================
HEAP ANALYSIS RESULT
====================================
2 APPLICATION LEAKSDisplaying only 1 leak trace out of 2 with the same signature
Signature: ce9dee3a1feb859fd3b3a9ff51e3ddfd8efbc6
┬───
│ GC Root: Local variable in native code
│
...
  • LeakCanary会为每个集成其的app在手机上添加一个启动图标

  • LeakCanary每行日志对应一组具有相同签名的泄漏。LeakCanary在应用程序第一次使用该签名触发泄漏时将一行标记为new

  • 点击每个泄漏可以通过查看详细信息

4.4 Categorizing leaks 泄漏分类

LeakCanary会将应用中的内存泄漏分为两类:

  1. 应用程序泄漏
  2. Library 泄漏
    并在logcat打印的日志中分开
====================================
HEAP ANALYSIS RESULT
====================================
0 APPLICATION LEAKS====================================
1 LIBRARY LEAK...
┬───
│ GC Root: Local variable in native code
│
...

并在泄漏列表中进行标记为Library Leak

5、修复内存泄漏

内存泄漏是由于程序错误导致的APP对不再使用的对象进行引用
可以通过下面4个步骤来修复内存泄漏问题:

  1. Find the leak trace. 找到泄漏痕迹
  2. Narrow down the suspect references. 缩小可疑引用
  3. Find the reference causing the leak. 找到导致泄漏的引用
  4. Fix the leak. 修复泄漏问题
5.1 找到泄漏问题

leak trace 是最强引用路径的短名,即从垃圾回收器根路径到内存中持有的对象的路径引用链

  • 例:存储helper实例静态引用
class Helper {
}class Utils {public static Helper helper = new Helper();
}
  • 通过LeakCanary 进行检测
AppWatcher.objectWatcher.watch(Utils.helper)
  • 泄漏trace
┬───
│ GC Root: Local variable in native code
│
├─ dalvik.system.PathClassLoader instance
│    ↓ PathClassLoader.runtimeInternalObjects
├─ java.lang.Object[] array
│    ↓ Object[].[43]
├─ com.example.Utils class
│    ↓ static Utils.helper
╰→ java.example.Helper

通过日志我们知道是Utils中泄漏了helper实例

5.2 缩小可疑引用范围
  • 例:
class ExampleApplication : Application() {val leakedViews = mutableListOf<View>()
}class MainActivity : Activity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.main_activity)val textView = findViewById<View>(R.id.helper_text)val app = application as ExampleApplication// This creates a leak, What a Terrible Failure!app.leakedViews.add(textView)}
}
  • leak trace
┬───
│ GC Root: System class
│
├─ android.provider.FontsContract class
│    ↓ static FontsContract.sContext
├─ com.example.leakcanary.ExampleApplication instance
│    ↓ ExampleApplication.leakedViews
├─ java.util.ArrayList instance
│    ↓ ArrayList.elementData
├─ java.lang.Object[] array
│    ↓ Object[].[0]
├─ android.widget.TextView instance
│    ↓ TextView.mContext
╰→ com.example.leakcanary.MainActivity instance
┬───
│ GC Root: System class
│
├─ android.provider.FontsContract class
│    ↓ static FontsContract.sContext
├─ com.example.leakcanary.ExampleApplication instance
│    Leaking: NO (Application is a singleton)
│    ↓ ExampleApplication.leakedViews
│                         ~~~~~~~~~~~
├─ java.util.ArrayList instance
│    ↓ ArrayList.elementData
│                ~~~~~~~~~~~
├─ java.lang.Object[] array
│    ↓ Object[].[0]
│               ~~~
├─ android.widget.TextView instance
│    Leaking: YES (View.mContext references a destroyed activity)
│    ↓ TextView.mContext
╰→ com.example.leakcanary.MainActivity instance

通过分析leak trace,显示是MainActivity 里面的textView的mContext一直持有引用,导致MainActivity销毁后无法释放

5.3 找到泄漏的引用

通过前面的分析我们知道了ArrayList.elementData和Object[].[0]是实现细节,实现中ArrayList不太可能存在错误ArrayList,因此导致泄漏的引用是唯一剩余的引用:ExampleApplication.leakedViews

┬───
│ GC Root: System class
│
├─ android.provider.FontsContract class
│    ↓ static FontsContract.sContext
├─ com.example.leakcanary.ExampleApplication instance
│    Leaking: NO (Application is a singleton)
│    ↓ ExampleApplication.leakedViews
│                         ~~~~~~~~~~~
├─ java.util.ArrayList instance
│    ↓ ArrayList.elementData
│                ~~~~~~~~~~~
├─ java.lang.Object[] array
│    ↓ Object[].[0]
│               ~~~
├─ android.widget.TextView instance
│    Leaking: YES (View.mContext references a destroyed activity)
│    ↓ TextView.mContext
╰→ com.example.leakcanary.MainActivity instance
5.4 修复泄漏问题

通过分析我们找到了泄漏点,解决方法也很简单,直接将application中leakedViews在的destory()方法中置空就行了

参考:https://square.github.io/leakcanary/

LeakCanary 使用及原理分析相关推荐

  1. Android LeakCanary原理分析

    概述 在上一篇LeakCanary使用详细教程中,我们熟悉了LeakCanary的使用和初步描述了它的工作机制,这篇我准备从源码的角度去分析LeakCanary的工作原理: 源码分析 从上一篇中我们知 ...

  2. java signature 性能_Java常见bean mapper的性能及原理分析

    背景 在分层的代码架构中,层与层之间的对象避免不了要做很多转换.赋值等操作,这些操作重复且繁琐,于是乎催生出很多工具来优雅,高效地完成这个操作,有BeanUtils.BeanCopier.Dozer. ...

  3. Select函数实现原理分析

    转载自 http://blog.chinaunix.net/uid-20643761-id-1594860.html select需要驱动程序的支持,驱动程序实现fops内的poll函数.select ...

  4. spring ioc原理分析

    spring ioc原理分析 spring ioc 的概念 简单工厂方法 spirng ioc实现原理 spring ioc的概念 ioc: 控制反转 将对象的创建由spring管理.比如,我们以前用 ...

  5. 一次 SQL 查询优化原理分析(900W+ 数据,从 17s 到 300ms)

    点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试文章 来源:Muscleape jianshu.com/p/0768eb ...

  6. 原理分析_变色近视眼镜原理分析

    随着眼镜的发展,眼镜的外型变得越来越好看,并且眼镜的颜色也变得多姿多彩,让佩戴眼镜的你变得越来越时尚.变色近视眼镜就是由此产生的新型眼镜.变色镜可以随着阳光的强弱变换不同的色彩. 变色眼镜的原理分析 ...

  7. jieba分词_从语言模型原理分析如何jieba更细粒度的分词

    jieba分词是作中文分词常用的一种工具,之前也记录过源码及原理学习.但有的时候发现分词的结果并不是自己最想要的.比如分词"重庆邮电大学",使用精确模式+HMM分词结果是[&quo ...

  8. EJB调用原理分析 (飞茂EJB)

    EJB调用原理分析 EJB调用原理分析 作者:robbin (MSN:robbin_fan AT hotmail DOT com) 版权声明:本文严禁转载,如有转载请求,请和作者联系 一个远程对象至少 ...

  9. 深入掌握Java技术 EJB调用原理分析

      深入掌握Java技术 EJB调用原理分析     一个远程对象至少要包括4个class文件:远程对象:远程对象的接口:实现远程接口的对象的stub:对象的skeleton这4个class文件. 在 ...

最新文章

  1. 信阳农林技术学院经纬度_信阳农林学院全景-360度,720度,高清全景地图-expoon网展...
  2. Reference和ReferenceQueue
  3. ubuntu下svn命令
  4. linux 递归修改所有权限
  5. php 替换某个字符,php中如何替换字符串中的某个字符-PHP问题
  6. python __getitem__()方法==>可以直接通过P[key]做运算
  7. cocosstdio之字体之文本和FNT字体
  8. keepalived mysql双主架构图_MySQL数据库架构和同步复制流程
  9. 艾伟:微软是在向谁“献刀”
  10. UVa 455 - Periodic Strings
  11. 单模光纤和多模光纤的区别,及光纤收发机(光电收发器)的介绍
  12. 基于SSM开发的房屋租赁系统 JAVA
  13. 世界著名汽车标志(大全)
  14. Java中实体类名称后缀VO,DTO的含义
  15. Going Deeper with Contextual CNN for Hyperspectral Image Classification
  16. C语言实现模拟银行存取款管理系统课程设计(纯C语言版)
  17. 图像处理(八)证件照蓝底换成红底,白底
  18. Java Vector API的使用测试
  19. C语言课设飞机票订购系统
  20. SQL基础编程——介绍及基本语法了解

热门文章

  1. 专接本计算机专业课难吗,河北计算机专业专接本难吗
  2. 20154313 刘文亨 EXP6
  3. 使用nginx问题记录(1):2021-03-29 nginx: [emerg] unknown directive “锘? in C:\nginx-1.16.1/conf/nginx.conf:3
  4. 景联文科技:语音工程系列(一)——语音标注的应用场景
  5. Java中的互斥锁介绍
  6. Chrome-使用代理-proxy-SwitchySharp的安装与设置
  7. 希捷原装移动硬盘拆壳
  8. UML之3——图(简介)
  9. selenium获取京东前三页奶瓶信息
  10. 免费的线框工具,UI设计工具,PDFs,资源等