内存之-LeakCanary

关于作者:CSDN内容合伙人、技术专家, 从零开始做日活千万级APP。
专注于分享各领域原创系列文章 ,擅长java后端、移动开发、人工智能等,希望大家多多支持。

目录

  • 一、导读
  • 二、概览
  • 三、使用
  • 四、原理分析
    • 4.1 自动初始化
      • 4.1.1 初始化
    • 4.2 LeakCananry自动检测步骤
      • 4.2.1 检测泄漏
      • 4.2.2 dump
      • 4.2.3 shark分析
      • 4.2.4 生成报告
  • 五、 推荐阅读

在这里插入图片描述

一、导读

我们继续总结学习基础知识,温故知新。
本文主讲 LeakCanary 使用及原理。

截止本文写稿,目前LeakCanary已经更新到2.12的版本, 我们基于2.x的版本来查看源码。

二、概览

LeakCanary主要有两大作用,第一发现内存泄漏问题,第二根据内存的状态输出泄漏的堆栈。

LeakCanary 的核心原理是主要通过 Android 生命周期的 api 来监听 activities 和 fragments 什么时候被销毁,
被销毁的对象会被传递给一个 ObjectWatcher,它持有它们的弱引用,默认等待5秒后观察弱引用是否进入关联的引用队列,
是则说明未发生泄露,否则说明可能发生泄漏。

LeakCanary 是我们熟悉内存泄漏检测工具,它能够帮助开发者非常高效便捷地检测 Android 中常见的内存泄漏。
在各大厂自研的内存泄漏检测框架(如腾讯 Matrix 和快手 Koom)的帮助文档中,也会引述 LeakCanary 原理分析。

LeakCanary Github
LeakCanary 官网

在Java中有四大引用,具体可查看下面这篇文章
强引用:绝不回收
软引用:内存不足才回收
弱引用:GC 就回收
虚引用:等价于没有引用,只是用来标识下指向的对象是否被回收。

三、使用

To use LeakCanary, add the leakcanary-android dependency to your app’s build.gradle file:

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

这样就可以了,LeakCanary会自动完成初始化,自动检测以下对象的泄漏:
Activity 、Fragment 、fragment View 、ViewModel 、Service等

我们也可以监听自己想要监听的任意对象,使用方式如下:

AppWatcher.objectWatcher.watch(object, "what you want to watcher")

四、原理分析

4.1 自动初始化

利用ContentProvider原理,ContentProvider的onCreate是在 Application的onCreate之前执行
因此在App进程拉起时会自动执行 AppWatcherInstaller 的onCreate生命周期,利用Android这种机制就可以完成自动初始化。

我们也可以关闭自动注册,进行手动注册。只需要在资源文件里覆写 @bool/eak_canary_watcher_auto_install 布尔值来关闭自动初始化,

<application><providerandroid:name="leakcanary.internal.MainProcessAppWatcherInstaller"android:authorities="${applicationId}.leakcanary-installer"android:enabled="@bool/leak_canary_watcher_auto_install"android:exported="false"/>
</application><resources><bool name="leak_canary_watcher_auto_install">false</bool>
</resources>

手动调用 AppWatcher.manualInstall 。

4.1.1 初始化

在初始化时,需要做以下几个操作

  1. 注册前后台切换监听、前台 Activity 监听和 ObjectWatcher 的泄漏监听
  2. 注册Activity的生命周期监听、fragment的生命周期监听、service等监听

我们来看看源码,都有哪些监听

  fun appDefaultWatchers(application: Application,reachabilityWatcher: ReachabilityWatcher = objectWatcher): List<InstallableWatcher> {return listOf(ActivityWatcher(application, reachabilityWatcher),                    // activityFragmentAndViewModelWatcher(application, reachabilityWatcher),        // fragmentRootViewWatcher(reachabilityWatcher),ServiceWatcher(reachabilityWatcher))}

在这个方法中,添加了我们常用的4中监听:

  • Fragment的生命周期期监听:同样,注册** FragmentManager.FragmentLifecycleCallbacks** ,但Fragment较为复杂,因为Fragment有三种,
    即android.app.Fragment、androidx.fragment.app.Fragment、android.support.v4.app.Fragment,因此需要注册各自包下的FragmentManager.FragmentLifecycleCallbacks;
  • view
  • service
  • Activity的生命周期监听:注册 Application.ActivityLifecycleCallbacks

class ActivityWatcher(private val application: Application,private val reachabilityWatcher: ReachabilityWatcher
) : InstallableWatcher {private val lifecycleCallbacks =object : Application.ActivityLifecycleCallbacks by noOpDelegate() {override fun onActivityDestroyed(activity: Activity) {reachabilityWatcher.expectWeaklyReachable(activity, "${activity::class.java.name} received Activity#onDestroy() callback")}}override fun install() {application.registerActivityLifecycleCallbacks(lifecycleCallbacks)}override fun uninstall() {application.unregisterActivityLifecycleCallbacks(lifecycleCallbacks)}
}

4.2 LeakCananry自动检测步骤

  1. 检测可能泄漏的对象;

  2. 堆快照,生成hprof文件;

  3. 分析hprof文件;

  4. 对泄漏进行分类。

4.2.1 检测泄漏

当在初始化时各种监听注册好之后,就到对象的监听者ObjectWatcher上场了。

利用引用对象可感知对象垃圾回收的机制判定内存泄漏: 为无用对象包装弱引用,并在一段时间后(默认为五秒)观察弱引用是否如期进
入关联的引用队列,是则说明未发生泄漏,否则说明发生泄漏(无用对象被强引用持有,导致无法回收,即泄漏)

@Synchronized fun watch(watchedObject: Any,description: String) {if (!isEnabled()) {return}removeWeaklyReachableObjects()val key = UUID.randomUUID().toString()val watchUptimeMillis = clock.uptimeMillis()val reference =KeyedWeakReference(watchedObject, key, description, watchUptimeMillis, queue)SharkLog.d {"Watching " +(if (watchedObject is Class<*>) watchedObject.toString() else "instance of ${watchedObject.javaClass.name}") +(if (description.isNotEmpty()) " ($description)" else "") +" with key $key"}watchedObjects[key] = referencecheckRetainedExecutor.execute {moveToRetained(key)}}

这里其实就用到了弱引用的第二个构造方法,我们注意上面源码中的KeyedWeakReference及queue,如果弱引用关联的的对象被回收,则会把这个弱引用加入到queue中,利用这个机制可以在后续判断对象是否被回收。
我们一起来看看WeakReference构造方法

public WeakReference(T referent) {super(referent);
}/*** 当GC回收对象时,将引用对象回收而将被引用对象放入ReferenceQueue*/
public WeakReference(T referent, ReferenceQueue<? super T> q) {super(referent, q);
}

对于检测的入口方法在private fun moveToRetained(key: String),当检测到泄漏后就进行dump,其步骤为:
1、移除不可达对象:移除** ReferenceQueue** 中记录的KeyedWeakReference 对象(引用着监听的对象实例);
2、主动触发GC:回收不可达的对象;
3、再次移除不可达对象:经过一次GC后可以进一步导致只有WeakReference持有的对象被回收;
4、判断是否还有剩余的监听对象存活,且存活的个数是否超过阈值;
5、若满足上面的条件,则抓取Hprof文件

调用的接口为onObjectRetained,可自行跟踪代码查看

fun interface OnObjectRetainedListener {/*** A watched object became retained.*/fun onObjectRetained()...
}

4.2.2 dump

Debug.dumpHprofData(path);

    // 最终调用 Debug.dumpHprofData(heapDumpFile.absolutePath) configProvider().heapDumper.dumpHeap(heapDumpFile)

关于hprof文件的内容会比较多,可自行学习

4.2.3 shark分析

Leakcanary2.x版本开源了自己实现的hprof文件解析以及泄漏引用链查找的功能模块(命名为shark),之前使用的是HAHA库,但是存在一些问题。

private fun analyzeHeap(heapDumpFile: File,progressListener: OnAnalysisProgressListener,isCanceled: () -> Boolean
): HeapAnalysis {...// Shark 堆快照分析器val heapAnalyzer = HeapAnalyzer(progressListener)...// 构建对象图信息val sourceProvider = ConstantMemoryMetricsDualSourceProvider(ThrowingCancelableFileSourceProvider(heapDumpFile)val graph = sourceProvider.openHeapGraph(proguardMapping = proguardMappingReader?.readProguardMapping())...// 开始分析heapAnalyzer.analyze(heapDumpFile = heapDumpFile,graph = graph,leakingObjectFinder = config.leakingObjectFinder, // 默认是 KeyedWeakReferenceFinderreferenceMatchers = config.referenceMatchers, // 默认是 AndroidReferenceMatcherscomputeRetainedHeapSize = config.computeRetainedHeapSize, // 默认是 trueobjectInspectors = config.objectInspectors, // 默认是 AndroidObjectInspectorsmetadataExtractor = config.metadataExtractor // 默认是 AndroidMetadataExtractor)
}

4.2.4 生成报告

进过一系列的分析后,就会生成一份报告。

五、 推荐阅读

Java 专栏

SQL 专栏

数据结构与算法

Android学习专栏

ddd

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/577219.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

人工智能_机器学习074_SVM支持向量机_软间隔与优化目标函数构建_C参数由来_惩罚误差点的惩罚度---人工智能工作笔记0114

然后我们接着上一节再来看一下这里我们说有个 min_faces_per_person = 0 这个可以看到如果我们写上0,就意味着要加载所有的人脸图片,就会花费的时间久对吧 我们可以试试,这里我们 min_faces_per_person = 0 改成0然后 我们等一会加载完了以后,我们用 display(X.shape,faces.sh…

Jenkins安装与设置(插件安装失败,版本问题解决)

早期的使用docker安装jenkins的方法会出现插件无法安装的问题&#xff0c;是由于docker拉取的jenkins版本太低了 jdk安装 Linux系统安装JDK1.8 详细流程 maven安装&#xff1a; centos7下安装Maven 使用docker进行安装jenkins&#xff1a; 先把镜像和容器卸干净 docker ps -a…

LinuxPTP在汽车时钟同步网络中的实践

这里列举一些在汽车以太网中使用LinuxPTP的注意点。 请使用较新版本的LinuxPTP&#xff0c;可以自己编译部署&#xff01; 在使用中&#xff0c;我遇到了某些指令或者配置文件关键字已经废弃&#xff0c;官方网页中的文档和实际工具对不上的问题。 研究发现是目标板中部署的Li…

vue data变量不能以“_”开头,否则会产生很多怪异问题

1、 比如给子组件赋值&#xff0c;子组件无法得到这个值&#xff08;也不是一直无法得到&#xff0c;设置后this.$forceUpdate() 居然可以得到&#xff09;&#xff0c; 更无法watch到 <zizujian :config"_config1"> </zizujian>this._config1 { ...…

python使用easyocr识别文字,准确率超高!

文章目录 一、文档二、使用1、安装2、使用3、下载模型4、配合pyautogui识别屏幕的文字 参考资料 一、文档 https://www.jaided.ai/easyocr/documentation/ 二、使用 1、安装 pip install easyocr2、使用 import easyocr# 创建EasyOCR Reader reader easyocr.Reader([ch_s…

Go 错误处理

Go 错误处理 Go 语言通过内置的错误接口提供了非常简单的错误处理机制。 error类型是一个接口类型&#xff0c;这是它的定义&#xff1a; type error interface {Error() string }我们可以在编码中通过实现 error 接口类型来生成错误信息。 函数通常在最后的返回值中返回错误…

python中的 multiprocessing.Event是什么

multiprocessing.Event 是 Python 中 multiprocessing 模块提供的一种同步原语&#xff0c;用于在多个进程之间传递信号。Event 本质上是一个用于线程/进程通信的信号标志&#xff0c;可以用于在不同进程之间进行事件的同步。 文章目录 创建 Event 对象在一个进程中设置 Event在…

短视频矩阵系统的崛起和影响

近年来&#xff0c;短视频矩阵系统已经成为了社交媒体中的一股新势力。这个新兴的社交媒体形式以其独特的魅力和吸引力&#xff0c;迅速吸引了大量的用户。这个系统简单来说就是将海量短视频整合在一个平台上&#xff0c;使用户可以方便地观看和分享好玩有趣的短视频。 短视频…

观察者模式学习

观察者模式&#xff08;Observer Design Pattern&#xff09;也被称为发布订阅模式&#xff08;Publish-Subscribe Design Pattern&#xff09;。在 GoF 的《设计模式》一书中&#xff0c;它的定义是这样的&#xff1a; Define a one-to-many dependency between objects so th…

50个免费的 AI 工具,提升工作效率(附网址)

上次我们已经介绍了20个精选的提高工作效率的免费AI工具&#xff0c;但如果你觉得这些AI工具还不过瘾的话&#xff0c;想进一步成为职场中最了解AI的人&#xff0c;本文将汇总介绍免费最新的50个AI工具。 DeepSwap DeepSwap 是一个基于 AI 的工具&#xff0c;适用于想要制作令人…

【内存泄漏】内存泄漏及常见的内存泄漏检测工具介绍

内存泄漏介绍 什么是内存泄漏 内存泄漏是指程序分配了一块内存&#xff08;通常是动态分配的堆内存&#xff09;&#xff0c;但在不再需要这块内存的情况下未将其释放。内存泄漏会导致程序浪费系统内存资源&#xff0c;持续的内存泄漏还导致系统内存的逐渐耗尽&#xff0c;最…

android内存管理机制概览

关于作者&#xff1a;CSDN内容合伙人、技术专家&#xff0c; 从零开始做日活千万级APP。 专注于分享各领域原创系列文章 &#xff0c;擅长java后端、移动开发、人工智能等&#xff0c;希望大家多多支持。 目录 一、导读二、概览三、相关概念3.1 垃圾回收3.2 应用内存的分配与回…

插入排序详解(C语言)

前言 插入排序是一种简单直观的排序算法&#xff0c;在小规模数据排序或部分有序的情况下插入排序的表现十分良好&#xff0c;今天我将带大家学习插入排序的使用。let’s go ! ! ! 插入排序 插入排序的基本思想是将待排序的序列分为已排序和未排序两部分。初始时&#xff0c…

商务大厦安装电气火灾监控系统,从源头监控电气火灾

安科瑞电气股份有限公司 上海嘉定 201801 摘要&#xff1a;介绍分析剩余电流式电气火灾监控系统的特点、组成、设计依据、监控原理和实施方案&#xff0c;并结合上海市某大厦工程设计实例探讨该系统在高层建筑中的设置要求和应用方式&#xff0c;供设计人员参考。 关键词&…

蓝桥杯嵌入式LED

1.LED原理图 2.CubeMX的LED的GPIO 3.创建LED.c和.h文件添加到bsp文件 添加bsp文件路径在c/c中 4.LED相关代码

Django之DRF框架三,序列化组件

一、序列化类的常用字段和字段参数 常用字段 字段名字段参数CharFieldmax_lengthNone, min_lengthNone, allow_blankFalse, trim_whitespaceTrueIntegerFieldmax_valueNone, min_valueNoneFloatFieldmax_valueNone, min_valueNoneBooleanFieldNullBooleanFieldFloatFieldmax_…

【隐私保护】使用Python从文本中删除个人信息:第一部分

自我介绍 做一个简单介绍&#xff0c;酒架年近48 &#xff0c;有20多年IT工作经历&#xff0c;目前在一家500强做企业架构&#xff0e;因为工作需要&#xff0c;另外也因为兴趣涉猎比较广&#xff0c;为了自己学习建立了三个博客&#xff0c;分别是【全球IT瞭望】&#xff0c;【…

linux中主从复制的架构和读写分离的方式

读写分离 互相主从架构注意点 双主双从架构注意点 一主多从架构注意点 读写分离概念部署jdk环境上传文件&#xff0c;解压文件配置环境变量 部署mycat环境mycat配置文件给所有数据库创建访问用户配置 server.xml配置 schema.xml负载均衡测试 互相主从 架构 iproleip1ip2的主库…

【开源】基于JAVA语言的大学生相亲网站

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块三、系统展示四、核心代码4.1 查询会员4.2 查询相亲大会4.3 新增留言4.4 查询新闻4.5 新增新闻 五、免责说明 一、摘要 1.1 项目介绍 基于JAVAVueSpringBootMySQL的大学生相亲网站&#xff0c;包含了会员管理模块、新闻管…

如何使用GPT4写一篇综述

使用 GPT-4 或任何其他高级语言模型来撰写一篇综述文章&#xff0c;需要遵循一系列的步骤来确保内容的准确性、深度和组织性。以下是一些指导步骤&#xff1a; 确定主题和范围 明确你想要综述的主题。这可以是一个科学领域的特定方面、技术发展、理论进展等。 确定综述的范围和…