Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >笔记13 - Android中的内存泄漏如何优化

笔记13 - Android中的内存泄漏如何优化

作者头像
码农帮派
发布于 2021-01-28 02:07:53
发布于 2021-01-28 02:07:53
1.4K00
代码可运行
举报
文章被收录于专栏:码农帮派码农帮派
运行总次数:0
代码可运行

Activity内存泄漏的预防

Activity承载了应用和用户交互的任务,在Activity中有大量的资源引用和上下文Context这样占用内存较大的资源对象,因为Activity一旦因为外部变量的持有,就会造成比较严重的内存泄漏。造成Activity内存泄漏的场景有以下:

1. 将Context或者View设置为static

View会默认持有一个Context的引用,如果将View设置为static会导致View在方法区无法被快速回收,从而造成Activity的内存泄漏:

上面代码中,由于imageView被设置为static,会导致ActivityB无法被回收。

2. 未解注册的各种Listener

我们在Activity中会注册各种系统监听器,比如广播:

当我们退出ActivityC,系统Destroy Activity的时候,会提示有内存泄漏:

3. 非静态Handler导致Activity泄漏

上面代码中的Handler会在一定情况下造成Activity的内存泄漏,我们知道Handler的执行需要借助于Looper和MessageQueue,当我们退出Activity,而MessageQueue中还有未被执行的任务,此时退出Activity,GC并不会立即回收Handler,而Handler持有外部Activity的引用,这样就会导致Activity无法被回收,还表现为该Activity的onDestroy方法未被执行。

所以在Activity中的Handler一般我们需要将其设置为static,然后在Handler内部持有一个Activity的弱引用,以此来避免内存泄漏。

4. 第三方库使用Context

我们项目中使用的第三方库的初始化有时候需要依赖Context对象,而且初始化的Context可能一直被持有,假如我们在初始化的时候传入了Activity的Context,就会导致Activity在退出之后无法被回收造成内存泄漏。为了防止这种情况,我们应该使用Application的Context进行初始化。

另外要是我们自己开发第三方库,在初始化前也应该开发者传入的Context进行判断,获取ApplicationContext:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Context appContext = context.getApplicationContext();

这样即使开发者传入了Activity,也可以通过该Activity获取到ApplicationContext,避免出现Activity泄漏的情况。

内存泄漏的检查

LeakCanary是Square公司提供的,可以检测App运行过程中内存泄漏的工具,当内存发生泄漏的时候,LeakCanary会生成内存泄漏对象的引用链,并可以通知到开发人员。

如何检测内存泄漏

Java中的WeakReference是弱引用类型,每次GC的时候,弱引用持有的对象如果没有被强引用持有,那么GC会回收它所持有的对象:

上述代码中构建了一个BigObject对象,但这个对象并没有被强引用持有,在System.gc()之后,该对象就会被回收。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
before gc, reference.get is com.xxxxx.WeakRefDemo$BigObject@7852e922
after gc, reference.get is null

WeakReference的构造函数允许我们传入一个ReferenceQueue,当WeakReference持有的对象被GC回收的时候,会将WeakReference放入到这个ReferenceQueue中:

运行代码之后,可以看到,在触发GC之后BigObject被成功回收,而且一个WeakReference对象也被放入到了ReferenceQueue中。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
before gc, reference.get is com.xxxxx.WeakRefDemo$BigObject@7852e922
before gc, queue is null
after gc, reference.get is null
after gc, queue is java.lang.ref.WeakReference@4e25154f

下面我们使用一个强引用来持有BigObject,看看GC是否可以回收对象:

上面的代码中BigOBject对象被一个强引用持有,即使bigObject对象被WeakReference修饰,GC之后并没有回收这个对象,所以不会将这个对象的WeakReference放入到ReferenceQueue中。

LeakCanary的实现思路

LeakCanary堆内存泄漏检测的核心是WeakReference和ReferenceQueue。

  • 1. 当一个Activity需要被回收的时候,会将其包装成一个WeakReference,并在WeakReference的构造函数中传入一个ReferenceQueue;
  • 2. 给包装后的WeakReference多一个标记Key,并且在一个强引用Set中添加这个Key的记录;
  • 3. 主动触发GC,GC之后遍历ReferenceQueue中的所有记录,将ReferenceQueue中有记录的Reference从Set中删除

我们之后在系统GC之后,没有被强引用持有的弱引用对象会被回收,回收之后的WeakReference会被放入到ReferenceQueue中,这样要是我们记录的应该被回收的对象清单Set中,除了ReferenceQueue中存在的已被回收的对象之外,剩余的就是应该被回收但并没有被成功回收的,这些对象就是发生了内存泄漏。

LeakCanary源码分析

从上面的分析可以知道利用WeakReference和ReferenceQueue可以实现内存泄漏的监控,但是如何知道一个Activity应该被回收了呢,一般情况下,当一个Activity的onDestroy被调用的时候,我们就认为一个Activity处于无用状态可以被回收了,因此我们需要监听每个Activity的onDestroy的调用情况。

LeakCancary中监听Activity生命周期是由ActivityRefWatch完成的,通过注册Android系统提供的ActivityLifecycleCallbacks。来监听Activity的生命周期:

上面LeakCanary通过registerActivityLifecycleCallbacks方法注册了对Activity生命周期的监听,传入了一个lifecycleCallbacks:

可以看到LeakCanary对Activity的onDestroy进行了监听,在Activity调用了onDestroy的时候,将这个Activity通知到RefWatcher中。

RefWatcher是LeakCanary的核心类,用来监控一个Activity是否发生了内存泄漏。在RefWatcher中会将即将被回收的Activity用WeakReference封装,并为它生成一个UUID,记录到retainedKeys中,用来保存应该被回收的Activity的记录。

接着Leakcanary会遍历ReferenceQueue中被回收对象,并将遍历到的对象的Key从retainedKeys中删除,剩余的长时间存在retainedKeys中的就是发生了内存泄漏未被回收的对象,LeakCanary会生成内存泄漏报告进行上报。

内存泄漏检查的时机

内存泄漏检测是比较耗时的,LeakCanary为了减少内存泄漏检查对UI渲染、以及业务逻辑的影响,使用了idleHandler。

我们知道Activity在启动之后会通过Looper.loop()阻塞的读取消息,当Looper的MessageQueue中没有消息的时候,线程会处于阻塞休眠的状态,我们如何知道主线程的Looper中没有消息可处理了呢,这个地方就要使用idleHandler了,LeakCanary会向主线程的MessageQueue中插入一个idleHandler,idleHandler只有在主线程处于空闲休眠的时候,才会被Looper从MessageQueue中取出执行,LeakCanary利用idleHandler有效的避免了占用主线程渲染时间。

LeakCanary检测其他类型的对象

LeakCanary默认只检测Activity的泄漏,但是RefWatcher的watch方法允许传入一个Object,这样LeakCanary实际上是可以检测任意类型对象的内存泄漏的。LeakReference的install方法会返回一个RefWatcher对象,我们可以将这个对象保存在Application中,然后将我们需要进行内存泄漏监控的对象传入到RefWatcher的watch方法中即可。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-01-14,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 码农帮派 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
LeakCanary- 如何检测 Activity 是否泄漏
为了将 LeakCanary 引入到我们的项目里,我们只需要做以下两步:(温馨提示:以下代码可以左右滑动查看)
开发者技术前线
2020/11/23
1.4K0
LeakCanary- 如何检测 Activity 是否泄漏
带你学开源项目:LeakCanary-如何检测活动是否泄漏
为了简单方便的检测内存泄漏,Square开源了LeakCanary,它可以实时监测活动是否发生了泄漏,一旦发现就会自动弹出提示及相关的泄漏信息供分析。
陈宇明
2020/12/15
7550
带你学开源项目:LeakCanary-如何检测活动是否泄漏
LeakCanary 原理剖析
内存泄漏是一个隐形炸弹,其本身并不会造成程序异常,但是随着量的增长会导致其他各种并发症:OOM,UI 卡顿等。
Erossssssss
2020/08/06
2.2K0
LeakCanary 原理剖析
Leakcanary 详解
LeakCanary的使用从LeakCanary.install(this)开始,
大发明家
2021/12/15
4090
Android 内存泄漏总结
内存管理的目的就是让我们在开发中怎么有效的避免我们的应用出现内存泄漏的问题。内存泄漏大家都不陌生了,简单粗俗的讲,就是该被释放的对象没有释放,一直被某个或某些实例所持有却不再被使用导致 GC 不能回收。最近自己阅读了大量相关的文档资料,打算做个 总结 沉淀下来跟大家一起分享和学习,也给自己一个警示,以后 coding 时怎么避免这些情况,提高应用的体验和质量。
阳仔
2019/07/31
5980
Android 内存泄漏总结
一步步拆解 LeakCanary
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/gdutxiaoxu/article/details/80752876
程序员徐公
2018/09/17
5200
一步步拆解 LeakCanary
LeakCanary源码浅析
在Android开发中最让人们头疼的就是内存泄漏了,今天来介绍一个查看内存是否泄漏的工具LeakCanary,并通过研究源码明白它是如何分析和查找存在泄漏信息的 首先送上LeakCanary文档链接:[LeakCanary中文使用说明](https://www.liaohuqiu.net/cn/posts/leak-canary-read-me/) Part1. 知识回顾 常用工具 1. Mat 2. LeakCanary(Square) 原理
用户1337002
2018/04/18
7300
全解系列:内存泄漏定位工具LeakCanary!
在日常开发中,不可避免的会遇到内存泄漏的问题,从而导致App的内存使用紧张,严重的情况还会导致App的卡顿甚至是奔溃,所以需要开发人员解决这些内存泄漏的问题。
胡飞洋
2020/09/17
5.5K0
Android内存泄漏分析
强引用:类似“Object obj = new Object()”这类的引用,只要强引用还存在,垃圾收集器永远不会回收掉被引用的对象。
用户1205080
2019/03/06
1.6K0
Android内存泄漏分析
LeakCanary笔记
RefWatcher 的代理类。通过注册 ActivityLifecycleCallbacks 回调,当 Activity 调用 onDestroy() 时进行一次内存泄漏检查,执行 RefWatcher 的 watch 方法,检测该 Activity 是否发生内存泄露。
续写经典
2018/08/28
3200
系统剖析Android中的内存泄漏
作为Android开发人员,我们或多或少都听说过内存泄漏。那么何为内存泄漏,Android中的内存泄漏又是什么样子的呢,本文将简单概括的进行一些总结。
技术小黑屋
2018/09/03
1.4K0
系统剖析Android中的内存泄漏
「Leakcanary 源码分析」看这一篇就够了
Reference 把内存分为 4 种状态,Active 、 Pending 、 Enqueued 、 Inactive。
程序亦非猿
2019/08/16
7480
「Leakcanary 源码分析」看这一篇就够了
Android内存泄漏分享
用户1172465
2018/01/05
1.2K0
Android内存泄漏分享
Android 优化——内存优化
在 GC 的过程中,其它在工作的线程会暂停,包括负责绘制的 UI 线程,并且在不同区域的内存释放速度也有一定的差异,但不管在哪个区域,都要到这次 GC 内存回收完成后,才会继续执行原来的线程。
三流之路
2018/09/11
1.5K0
LeakCanary源码解析
LeakCanary : https://github.com/square/leakcanary
俞其荣
2019/07/09
7220
Handler的5种内存泄漏场景
大家好,我是稳稳,一个曾经励志用技术改变世界,现在为随时失业做准备的中年奶爸程序员,与你分享生活和学习的点滴。
AntDream
2025/03/07
1280
Handler的5种内存泄漏场景
LeakCanary原理分析
概述 LeakCanary是一个开源的内存泄漏检测库,极大简化了内存泄漏的检测流程。了解其工作原理,有助于我们更好的理解Android的内存管理机制。 使用示例 在 build.gradle中添加配置: dependencies { debugImplementation 'com.squareup.leakcanary:leakcanary-android:1.6.3' releaseImplementation 'com.squareup.leakcanary:leakcanary-androi
用户1205080
2019/03/11
1.5K0
LeakCanary原理分析
Android-强,软,弱,虚引用
      啥是强引用?举个例子,我们平时new 的对象,就都是强引用。如: String s =new String(),这就是一个强引用,那么强引用有啥特点呢?
android_薛之涛
2018/09/12
4830
Android-强,软,弱,虚引用
内存泄漏三问—vivo真题
说到性能优化,就不得不提下内存泄漏了,内存泄漏发生的原因以及解决办法你是否都已了解呢?看看今天的三问:
码上积木
2020/09/27
5890
重谈Handler的内存泄漏
在多线程操作中,handler会使用的非常多,但是每次使用handler你有没有考虑内存泄漏的问题。
Yif
2019/12/26
1.2K0
相关推荐
LeakCanary- 如何检测 Activity 是否泄漏
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验