我的编程空间,编程开发者的网络收藏夹
学习永远不晚

Android LeakCanary检测内存泄露原理

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

Android LeakCanary检测内存泄露原理

以LeakCanary2.6源码分析LeakCanary检测内存泄露原理,为减少篇幅长度,突出关键点,不粘贴大量源码,阅读时需搭配源码食用。

如何获取context

LeakCanary只需引入依赖,不需要初始化代码,就能执行内存泄漏检测了,它是通过ContentProvider获取应用的context。这种获取context方式在开源第三方库中十分流行。如下AppWatcherInstaller在LeakCanary的aar包中manifest文件中注册。


internal sealed class AppWatcherInstaller : ContentProvider() {
 override fun onCreate(): Boolean {
  val application = context!!.applicationContext as Application
  AppWatcher.manualInstall(application)//1
  return true
 }
 ...
}

默认检测哪些类对象的内存泄露

(1)处的方法将调用如下方法注册需要检测泄露的对象:


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

可以看到LeakCanary会把Activity,Fragment,ViewModel,RootView和Service纳入检测,这些对象都是有明确的生命周期,而且占用内存较高,它们的内存泄露是需要我们重点关注的。

如何将这些生命周期对象纳入监测

(1)处的manualInstall方法将遍历调用上述Watcher的install方法以适时将这些生命周期对象纳入检测。

ActivityWatcher

ActivityWatcher中install方法通过向application注册Application.ActivityLifecycleCallbacks接口回调实现对Activity生命周期的检测。这里有一个很棒的技巧,利用Kotlin委托与Java动态代理,将不需要关注的方法给出默认空实现,(2)(3)处代码提取出来,可以在平时开发中有需求的地方使用。


//ActivityWatcher
 private val lifecycleCallbacks =
  object : Application.ActivityLifecycleCallbacks by noOpDelegate() {
   override fun onActivityDestroyed(activity: Activity) {
    reachabilityWatcher.expectWeaklyReachable(
     activity, "${activity::class.java.name} received Activity#onDestroy() callback"
    )//4
   }
  }

internal inline fun <reified T : Any> noOpDelegate(): T {
 val javaClass = T::class.java
 return Proxy.newProxyInstance(
  javaClass.classLoader, arrayOf(javaClass), NO_OP_HANDLER
 ) as T
}//2

private val NO_OP_HANDLER = InvocationHandler { _, _, _ ->
 // no op
}//3

(4)调用的objectWatcher.expectWeaklyReachable方法是将对象纳入监测的通用方法,如其名称所示,WeaklyReachable相较的是StronglyReachable,当一个对象不再需要时,我们希望它从WeaklyReachable变为StronglyReachable。

我们可以在不再需要某对象时主动调用该方法,检测任意对象(除上节的默认对象)的内存泄露:


AppWatcher.objectWatcher.expectWeaklyReachable(obj, "")

onActivityDestroyed回调中就通过该方式将activity纳入监测。

通过上述对Activity的纳入内存泄露源码的分析,可以发现其中2个关键点,首先需要能获取应用中所有待检测对象的引用,其次需要一个待检测对象生命周期结束的时机。而这两点通过注册Application.ActivityLifecycleCallbacks接口能够同时满足,可对于其他类对象,就没有如此便捷的方式了。

下面介绍Fragment,ViewModel,RootView和Service这些类对象是如何纳入检测的。

FragmentAndViewModelWatcher

Fragment为了兼容在Android源码中几个不同包名的实现,对它们的检测也需要分别实现,我们在FragmentAndViewModelWatcher中只关注AndroidXFragmentDestroyWatcher对AndroidX中Fragment的内存泄露检测即可,其他几个实现类似。

FragmentAndViewModelWatcher先同样通过注册Application.ActivityLifecycleCallbacks回调,适时获取Activity引用,并在AndroidXFragmentDestroyWatcher获取Activity的supportFragmentManager,向其注册FragmentManager.FragmentLifecycleCallbacks。在其中的onFragmentDestroyed与onFragmentViewDestroyed回调中将Fragment和Fragment的View纳入内存泄露检测。

对于ViewModel的检测,则需要关注ViewModelClearedWatcher,通过用上一步获取的Activity引用,添加名为ViewModelClearedWatcher的spy ViewModel,来获得收到onCleared回调的能力,因为对于一个ViewModelStoreOwner(Activity,Fragment)来说,自己的一个ViewModel回调了onCleared,则其他ViewModel的onCleared也应该被调用。这些ViewModel是通过ViewModelStore的mMap属性反射获取的。在spy ViewModel的onCleared回调中,纳入内存泄露检测。

RootViewWatcher

对于Android里Window中的RootView,即DecorView,可以通过注册addOnAttachStateChangeListener在View的onViewDetachedFromWindow时进行检测。而获取待检测对象的引用就不像Activity和Fragment一样有回调可以依赖了。LeakCanary采取了Hook的方式在install方法对RootView的容器进行替换,具体来说就是通过反射机制将WindowManagerGlobal中的mViews(包含所有Window中的DecorView)的ArrayList容器的实现修改,在其add方法中获取DecorView的引用,之后设置OnAttachStateChangeListener回调进行检测。

ServiceWatcher

而Android中Service,无论是获取引用还是监测时机的确定都没有系统的回调可以依赖,LeakCanary都是采用Hook的方式达到目的。首先通过反射拿到ActivityThread中的mServices,这是包含app中全部Service的一个Map。在install方法中有两个Hook点,首先是Android 消息机制的中转中心,名为H的Handler,系统侧对应用侧的全部回调都需要经过它的周转。因为Handler中mCallback执行的优先级大于handleMessage方法,Leakcanary替换H的mCallback实现,当消息为STOP_SERVICE时,便从mServices取出该消息对应的Service作为待检测Service引用。第二个Hook点为ActivityManagerService,通过动态代理修改它的serviceDoneExecuting方法,在其真正实现前增加内存泄露检测,其余方法保持不变。

这些类纳入检测纳入检测的时机,可总结为如下表格:

如何获取引用 何时纳入监测
Activity ActivityLifecycleCallbacks回调 onActivityDestroyed
Fragment FragmentLifecycleCallbacks回调 onFragmentDestroyed
Fragment中的View FragmentLifecycleCallbacks回调 onFragmentViewDestroyed
ViewModel 反射获取ViewModelStore的mMap spy ViewModel的onCleared
Window中的DecorView Hook WindowManagerGlobal中的mViews onViewDetachedFromWindow
Service Hook H的mCallback实现,当消息为STOP_SERVICE时,从ActivityThread中的mServices获取 Hook ActivityManagerService,serviceDoneExecuting中检测

如何确定内存泄露的对象

在确定待检测对象与时机后,查看ObjectWatcher的expectWeaklyReachable方法,可以得知如何实现将泄露对象从待检测对象(默认即上节我们分析的那些有生命周期的类对象)挑出来的。确定内存泄露对象的原理是我们常用的WeakReference,其双参数构造函数支持传入一个ReferenceQueue,当其关联的对象回收时,会将WeakReference加入ReferenceQueue中。LeakCanary的做法是继承ReferenceQueue,增加一个值为UUID的属性key,同时将每个需要监测的对象WeakReference以此UUID作为键加入一个map中。这样,在GC过后,removeWeaklyReachableObjects方法通过遍历ReferenceQueue,通过key值删除map中已回收的对象,剩下的对象就基本可以确定发生了内存泄露。

如何确定从GC root到泄露对象的引用链

在确定内存泄露的对象后,就需要其他手段来确定泄露对象引用链了,这一过程开始于checkRetainedObjects方法,跟踪调用可以看到启动了前台服务HeapAnalyzerService,这在我们使用LeakCanary时可以在通知栏看到。服务中调用了HeapAnalyzer的analyze方法进行堆内存分析,由Shark库实现该功能,就不再进行追踪。

以上就是分析LeakCanary检测内存泄露原理的详细内容,更多关于LeakCanary检测内存泄露的资料请关注编程网其它相关文章!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

Android LeakCanary检测内存泄露原理

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

Android LeakCanary检测内存泄露原理是什么

小编给大家分享一下Android LeakCanary检测内存泄露原理是什么,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!以LeakCanary2.6源码分析Le
2023-06-14

使用Android Studio检测内存泄露(LeakCanary)

内存泄露,是Android开发者最头疼的事。可能一处小小的内存泄露,都可能是毁千里之堤的蚁穴。 怎么才能检测内存泄露呢? AndroidStudio 中Memory控件台(显示器)提供了一个内存监视器。我们可以通过它方便地查看应用程序的性
2022-06-06

Android内存泄漏检测-LeakCanary

前言:Android内测泄漏是比较常见的问题,在没有造成OOM之前,在测试过程中,也会经常性的忽略这个问题,但在android碎片化严重的,还是存在很多内测泄漏造成OOM的问题在反馈。那么如何检测内存泄漏呢,当然我们可以通过MAT这些工具来
2022-06-06

Android内存泄漏检测工具LeakCanary

在Android的性能优化中,内存优化是必不可少的点,而内存优化最重要的一点就是解决内存泄漏的问题,在Android的内存泄漏分析工具也不少,比如PC端的有:AndroidStudio自带的Android Profiler、MAT等工具;手机端也有,就是我们今天要介绍的LeakCanary
2023-05-15

Android中LeakCanary检测内存泄漏的方法

最近要对产品进行内存泄漏的检查,最后选择了使用Square公司开源的一个检测内存泄漏的函数库LeakCanary,在github上面搜索了一下竟然有1.6w个star,并且Android大神JakeWharton也是这个开源库的贡献者。那么
2023-05-30

Android内存泄漏检测工具LeakCanary怎么使用

这篇“Android内存泄漏检测工具LeakCanary怎么使用”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“Android
2023-07-06

内存泄漏检测工具LeakCanary源码解析

这篇文章主要为大家介绍了内存泄漏检测工具LeakCanary源码解析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
2023-01-28

golang怎么检测内存是否泄露

在Go语言中,可以使用内置的`runtime`包来检测内存泄漏。具体的步骤如下:1. 导入`runtime`包:```goimport "runtime"```2. 在需要检测内存泄漏的地方,使用`runtime.GC()`函数触发一次垃圾
2023-09-27

源码浅析Android中内存泄漏检测工具Leakcanary的使用

大名鼎鼎的Leakcanary想必作为Android开发都多多少少接触过,新版本的Leakcanary也用Kotlin重写了一遍,最近详细查看了下源码,就来和大家简单分享一下
2023-05-17

golang函数内存泄露检测与解决

go 语言中存在函数内存泄露,它会导致应用程序内存不断消耗和崩溃。我们可以使用 runtime/pprof 包进行检测,并检查函数是否意外保留了对不需要资源的引用。要解决内存泄露,我们需要找到导致泄露的引用,通常通过检查函数代码和查找全局变
golang函数内存泄露检测与解决
2024-04-23

windows下C/C++内存泄露检测的方法

小编给大家分享一下windows下C/C++内存泄露检测的方法,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!检测内存泄露利用Visual Studio调试器和CR
2023-06-15

android内存泄露:1、LeakCanarys内存泄漏检测库、非静态的内部类错误使用

一、前言 今天来复习一下有关内存泄露的知识,一个是把之前笔记整理一下,如有一些工具版本更新会写一些当下的使用介绍。另外会参考网上比较好的博文进行修正改进一些知识点 二、内存泄露、内存溢出、内存抖动 首先我们必须对内存泄露有一个最简单的了解,
2022-06-06

PHP应用程序中的内存泄露:原因、检测和解决

php 内存泄露是指应用程序分配内存后未能释放,导致服务器可用内存减少和性能下降。原因包括循环引用、全局变量、静态变量和扩展。检测方法有 xdebug、valgrind 和 phpunit mock objects。解决步骤为:识别泄漏源、
PHP应用程序中的内存泄露:原因、检测和解决
2024-05-09

android内存泄露的根本原因是什么

Android内存泄漏的根本原因是因为对象在不再使用时未能被垃圾回收器正确地回收,导致内存持续占用和增加。这主要是因为以下几个常见情况:1. 静态引用:将对象以静态方式引用,使其生命周期超出了需要的范围,从而无法被垃圾回收器回收。2. 上下
2023-09-15

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

前言:最近公司C轮融资成功了,移动团队准备扩大一下,需要招聘Android开发工程师,陆陆续续面试了几位Android应聘者,面试过程中聊到性能优化中如何避免内存泄漏问题时,很少有人全面的回答上来。所以决定抽空学习总结一下这方面的知识,以及
2022-06-06

编程热搜

  • Android:VolumeShaper
    VolumeShaper(支持版本改一下,minsdkversion:26,android8.0(api26)进一步学习对声音的编辑,可以让音频的声音有变化的播放 VolumeShaper.Configuration的三个参数 durati
    Android:VolumeShaper
  • Android崩溃异常捕获方法
    开发中最让人头疼的是应用突然爆炸,然后跳回到桌面。而且我们常常不知道这种状况会何时出现,在应用调试阶段还好,还可以通过调试工具的日志查看错误出现在哪里。但平时使用的时候给你闹崩溃,那你就欲哭无泪了。 那么今天主要讲一下如何去捕捉系统出现的U
    Android崩溃异常捕获方法
  • android开发教程之获取power_profile.xml文件的方法(android运行时能耗值)
    系统的设置–>电池–>使用情况中,统计的能耗的使用情况也是以power_profile.xml的value作为基础参数的1、我的手机中power_profile.xml的内容: HTC t328w代码如下:
    android开发教程之获取power_profile.xml文件的方法(android运行时能耗值)
  • Android SQLite数据库基本操作方法
    程序的最主要的功能在于对数据进行操作,通过对数据进行操作来实现某个功能。而数据库就是很重要的一个方面的,Android中内置了小巧轻便,功能却很强的一个数据库–SQLite数据库。那么就来看一下在Android程序中怎么去操作SQLite数
    Android SQLite数据库基本操作方法
  • ubuntu21.04怎么创建桌面快捷图标?ubuntu软件放到桌面的技巧
    工作的时候为了方便直接打开编辑文件,一些常用的软件或者文件我们会放在桌面,但是在ubuntu20.04下直接直接拖拽文件到桌面根本没有效果,在进入桌面后发现软件列表中的软件只能收藏到面板,无法复制到桌面使用,不知道为什么会这样,似乎并不是很
    ubuntu21.04怎么创建桌面快捷图标?ubuntu软件放到桌面的技巧
  • android获取当前手机号示例程序
    代码如下: public String getLocalNumber() { TelephonyManager tManager =
    android获取当前手机号示例程序
  • Android音视频开发(三)TextureView
    简介 TextureView与SurfaceView类似,可用于显示视频或OpenGL场景。 与SurfaceView的区别 SurfaceView不能使用变换和缩放等操作,不能叠加(Overlay)两个SurfaceView。 Textu
    Android音视频开发(三)TextureView
  • android获取屏幕高度和宽度的实现方法
    本文实例讲述了android获取屏幕高度和宽度的实现方法。分享给大家供大家参考。具体分析如下: 我们需要获取Android手机或Pad的屏幕的物理尺寸,以便于界面的设计或是其他功能的实现。下面就介绍讲一讲如何获取屏幕的物理尺寸 下面的代码即
    android获取屏幕高度和宽度的实现方法
  • Android自定义popupwindow实例代码
    先来看看效果图:一、布局
  • Android第一次实验
    一、实验原理 1.1实验目标 编程实现用户名与密码的存储与调用。 1.2实验要求 设计用户登录界面、登录成功界面、用户注册界面,用户注册时,将其用户名、密码保存到SharedPreference中,登录时输入用户名、密码,读取SharedP
    Android第一次实验

目录