内存之-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.hqwc.cn/news/299483.html

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

相关文章

改善OEE的关键:从反应性维护向预测性维护转变

科技的进步正在对企业的日常运营模式产生影响。许多制造企业已经采用了自动化生产流程&#xff0c;这不仅提高了产品质量&#xff0c;还简化了设备维护流程&#xff0c;并使得制造企业的设备维护方式从反应性维护转变为预测性维护。人们发现&#xff0c;设备维护方式的转变显著…

前端通过不同方式画等腰梯形

1.css画 说明&#xff1a;css画出倒梯形 <div class"outer-box"><div class"gradient_bg"></div></div> .outer-box{width: 50px;height: 50px;position: relative;//overflow: hidden; } /*使用transform属性后&#xff0c;梯形…

[kubernetes]Kube-APIServer

API Server API Server是什么 提供集群管理的REST API接口&#xff0c;包括认证授权、数据校验以及集群状态变更等提供其他模块之间的数据交互和通信的枢纽&#xff08;其他模块通过API Server查询或修改数据&#xff0c;只有API Server才直接操作etcd&#xff09; 访问控制…

统计学常见概率分布

1.离散型随机变量 E ( X ) ∑ i 1 n x i p i E(X)\sum_{i1}^{n}x_ip_i E(X)i1∑n​xi​pi​ D ( X ) σ 2 E ( X 2 ) − [ E ( X ) ] 2 D(X)\sigma^2E(X^2)-[E(X)]^2 D(X)σ2E(X2)−[E(X)]2 1.1 二项分布 n n n个相同试验&#xff1b;每次试验只有2个可能的结果&#xff1…

插入排序详解(C语言)

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

介绍一个在线K8S练习平台

是不是有很多小伙伴想学习k8s&#xff0c;但是又没有机器去练习。使用自己的笔记本电脑或者主机只能搭建单机版本的k8s来练习。 现在福利来了&#xff0c;给大家介绍一个在线多节点k8s练习平台&#xff1a;Play with Kubernetes。 Play with Kubernetes 介绍 Play with Kube…

Modbus转Profinet网关解决设备通讯不稳的问题

通讯不稳定&#xff1a;表现为数据断断续续&#xff0c;多半是由于线路干扰、接口不匹配、程序不稳定、等原因造成。 解决方案&#xff1a;在原配电柜添加Modbus转Profinet网关&#xff08;XD-MDPN100/2000&#xff09;即可解决通迅该问题&#xff0c;Modbus转Profinet网关&…

isp代理/双isp代理/数据中心代理的区别?如何选择?

本文我们来详细科普一下几种不同的代理类型&#xff1a;isp代理/双isp代理/数据中心代理&#xff0c;了解他们的区别&#xff0c;选择更适合自己的代理类型。 在讲述这几种代理类型之前&#xff0c;我们先复习一下代理大类有哪几种。 一、机房代理和非机房代理 在做代理ip选…

Linux服务器流量监控、统计、限制、实时流量,按小时查询、按天数查询、按月数查询、按周数查询、查询TOP10等等各种纬度统计

Linux服务器流量监控、统计、限制、实时流量,按小时查询、按天数查询、按月数查询、按周数查询、查询TOP10等等各种纬度统计。 ServerStatus-V 是一个酷炫高逼格的云探针、云监控、服务器云监控、多服务器探针。使用方便,信息直观。ServerStatus-V 是 ServerStatus 中文版 项…

【Java动态代理如何实现】

✅Java动态代理如何实现 ✅JDK动态代理和Cglib动态代理的区别 ✅拓展知识仓✅静态代理和动态代理的区别✅动态代理的用途✅Spring AOP的实现方式&#x1f4d1;JDK 动态代理的代码段&#x1f4d1;Cglib动态代理的代码块 ✅注意事项&#xff1a; 在Java中&#xff0c;实现动态代理…

Dbeaver如何连接Oceanbase?

Dbeaver & Oceanbase 一、新增驱动二、连接数据库 一、新增驱动 1、新建驱动 点击数据库 -> 驱动管理器 -> 新建 2、设置驱动 驱动名称可随意填写注意驱动类型要是Generichost:port填写实际的host和port 库中新增下载的oceanbase驱动jar包 二、连接数据库 1、找…

c 语言,指针,指针的指针

c 语言&#xff0c;指针&#xff0c;指针的指针 指针就是指向变量地址的东西。 比如&#xff1a; 定义了一个 int 变量 p&#xff0c;值为 1 。定义了 int 指针 pInt 指向了变量 p&#xff0c; 它的名字前面有个 * &#xff0c;此时 pInt 就是 p 的地址&#xff0c;当前面加…