此文章来源于APP架构师这个公众号

今天的主题是Android开发中的内存泄漏,之所以说这个是因为前几天做了项目中的内存泄漏排查与解决,在这里总结一下,被提供一种快速定位解决Android内存泄漏的方案,希望大家看完有所收获。

1 奠基之石——内存泄漏概述

在介绍内存泄漏之前很有必要提及一下Android系统的垃圾回收机制。Java GC(Garbage Collection,垃圾收集,垃圾回收)机制,是Java与C++/C的主要区别之一,作为Java开发者,一般不需要专门编写内存回收和垃圾清理代码,对内存泄露和溢出的问题,也不需要像C程序员那样战战兢兢。这是因为在Java虚拟机中,存在自动内存管理和垃圾清扫机制。概括地说,该机制对虚拟机中的内存进行标记,并确定哪些内存需要回收,根据一定的回收策略,自动的回收内存,永不停息(Nerver Stop)的保证虚拟机中的内存空间,防止出现内存泄露和溢出问题。Android系统的垃圾回收是基于可达性分析算法(根搜索算法)的。如下图所示,从GC Roots(每种具体实现对GC Roots有不同的定义)作为起点,向下搜索它们引用的对象,可以生成一棵引用树,树的节点视为可达对象,反之视为不可达。
不可达的对象(如下图中的object5,6,7)会在系统GC的时候被回收,从而释放内存空间。

如果所有的对象都可以被顺利回收就没有本文的诞生了,举个简单的例子,我们在开发中经常使用单例模式,单例的静态特性导致其生命周期同应用一样长。有时创建单例时如果我们需要Context对象,如果传入的是Application的Context那么不会有问题。如果传入的是Activity的Context对象,那么当Activity生命周期结束时,该Activity的引用依然被单例持有,所以不会被回收,而单例的生命周期又是跟应用一样长,这个情况就叫做内存泄露(Memory Leak)。它指的是当你不再需要某个实例后,但是这个对象却仍然被引用,防止被垃圾回收(Prevent from being bargage collected)。

public class Util {private Context mContext;private static Util sInstance;private Util(Context context) {this.mContext = context;}public static Util getInstance(Context context) {if (sInstance == null) {sInstance = new Util(context);}return sInstance;}
}

本杰明 富兰克林曾说:A small leak will sink a great ship.意即:小漏不补沉大船。基于Android系统的设备一般来说内存就不大,特别是早期的Android设备,内存泄漏是很致命的,内存泄漏积攒到一定程度会引发内存溢出(OOM),如果处理不当直接导致程序崩溃退出。

2.了然于胸——常见的内存泄漏

一般来说在开发中我们经常会犯下下面几个错误,导致内存泄漏。这几个都是前人踩坑总结出来的,非常有参考价值,至少我在排查解决内存泄漏的时候是这样的。

一、单例造成的内存泄漏

Android的单例模式非常受开发者的喜爱,不过使用的不恰当的话也会造成内存泄漏。因为单例的静态特性使得单例的生命周期和应用的生命周期一样长,这就说明了如果一个对象已经不需要使用了,而单例对象还持有该对象的引用,那么这个对象将不能被正常回收,这就导致了内存泄漏。例子见上面那段代码。

二、非静态内部类创建静态实例造成的内存泄漏

有的时候我们可能会在启动频繁的Activity中,为了避免重复创建相同的数据资源,在Activity内部创建了一个非静态内部类的单例,每次启动Activity时都会使用该单例的数据,这样虽然避免了资源的重复创建,不过这种写法却会造成内存泄漏,因为非静态内部类默认会持有外部类的引用,而又使用了该非静态内部类创建了一个静态的实例,该实例的生命周期和应用的一样长,这就导致了该静态实例一直会持有该Activity的引用,导致Activity的内存资源不能正常回收。例子如下

public class MainActivity extends AppCompatActivity {private static TestResource mResource = null;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);if (mManager == null) {mManager = new TestResource();}//...}class TestResource {//...}}

三、Handler造成的内存泄漏

Handler的使用造成的内存泄漏问题应该说最为常见了,平时在处理网络任务或者封装一些请求回调等api都应该会借助Handler来处理,我们经常在Activity里面这样定义一个私有的Handler对象并初始化,这种创建Handler的方式会造成内存泄漏,由于mHandler是Handler的非静态匿名内部类的实例,所以它持有外部类Activity的引用,我们知道消息队列是在一个Looper线程中不断轮询处理消息,那么当这个Activity退出时消息队列中还有未处理的消息或者正在处理消息,而消息队列中的Message持有mHandler实例的引用,mHandler又持有Activity的引用,所以导致该Activity的内存资源无法及时回收,引发内存泄漏。

private Handler mHandler = new Handler() {@Overridepublic void handleMessage(Message msg) {//...}};

四、资源未关闭造成的内存泄漏

对于使用了BraodcastReceiver,ContentObserver,File,Cursor,Stream,Bitmap等资源的使用,应该在Activity销毁时及时关闭或者注销,否则这些资源将不会被回收,造成内存泄漏。

3 神兵利器——检测内存泄漏的常见工具

见到这个标题有经验的开发者可能要吐槽我是标题党了,特别是从Eclipse时代走过来的开发者,以为我一要开始贴那张像**一样的MAT内存模型图或者AndroidStudio中Monitors下的实时内存占用图,又要开始分析那一条条剪不断理还乱的内存引用链,然后费尽九牛二虎之力去查找项目中无数的内存泄漏中的一个。但是,我要告诉你,你错了。其实,以前我看到内存泄漏分析文章的时候也是这样的想法,看着恐怖的MAT内存模型图,觉得内存泄漏的排查和解决简直是Android开发中登峰造极的技能。直到我遇到了她——LeakCanary,我才直到原来内存泄漏的排查和解决可以那么的优雅。LeakCanary是Square开源了一个内存泄露自动探测神器 。这是项目的github仓库地址:https://github.com/square/leakcanary 。使用非常简单,在build.gradle中引入包依赖:

debugCompile 'com.squareup.leakcanary:leakcanary-
android:1.5'
releaseCompile 'com.squareup.leakcanary:leakcanary-
android-no-op:1.5'
testCompile 'com.squareup.leakcanary:leakcanary-
android-no-op:1.5'

在Application中的onCreate方法中增加初始化代码:

if (LeakCanary.isInAnalyzerProcess(this)) {// This process is dedicated to LeakCanary for// heap analysis.// You should not init your app in this process.return;
}
LeakCanary.install(this);

集成后什么都不用做,按照正常测试,当有内存泄漏发生后,应用会通过系统通知栏发出通知,点击通知就可以进入查看内存泄漏的具体信息。在这里举个实践中的例子。把LeakCanary集成到项目中后,等App启动后一会,系统通知到了,点击通知,跳转到泄漏的详情页面进行查看:

很明显,WebSiteQueryActivity泄露了。首先,static 的MaskHeadView.fLayout变量引用了FrameLayout.mContext对象,这个对象的引用就是指向了WebSiteQueryActivity的实例,导致了它的泄漏,在第二节中我们说过static对象是内部的static对象是比较容易造成内存泄漏的,检查代码发现,MaskHeadView直接在WebSiteQueryActivity的xml文件中使用了,因此持有WebSiteQueryActivity的实例,因为fLayout对象是静态的,因此它的生命周期与Application同样长,因此WebSiteQueryActivity退出后,它的实例引用依然被fLayout持有,导致它无法被回收从而内存泄露了。仔细检查代码,发现fLayout并没有被外部使用到,应该是之前的开发者手抖加了个static字段上去或者是现在不用了,但是没有去掉,在这里我直接去掉了这个修饰符,在此build代码,这个内存泄漏的现象消失了。

//去掉static修饰符,避免static对象引起的内存泄漏
private static FrameLayout fLayout;public MaskHeadView(Context context, AttributeSet attrs) {super(context, attrs);this.context=context;initView(context);
}private void initView(Context context2) {view = LayoutInflater.from(context).inflate(R.layout.mask_head_view, this);fLayout=(FrameLayout) view.findViewById(R.id.mask_container);
}

这只是个极简单的例子,但方法是一样的。顺便提一句,其实无论是MAT工具的内存分析,还是AndroidStudio中自带的分析工具亦或是LeakCanary,原理都是一样的,都是dump java heap出来进行分析,找到泄漏的问题,只是LeakCanary帮我们把分析的工作做了。
曾几何时,你以为内存泄漏分析都是这样的

但是现在你会发现其实也可以是酱紫的:

快速定位解决Android内存泄漏相关推荐

  1. Android内存泄漏查找和解决

    Android内存泄漏查找和解决 目录: 内存泄漏的概念 一个内存泄漏的例子 Java中"失效"的private修饰符 回头看内存泄漏例子泄漏的重点 强引用与弱引用 解决内部类的内 ...

  2. android释放acitity内存,Android 内存泄漏分析与解决方法

    在分析Android内存泄漏之前,先了解一下JAVA的一些知识 1. JAVA中的对象的创建 使用new指令生成对象时,堆内存将会为此开辟一份空间存放该对象 垃圾回收器回收非存活的对象,并释放对应的内 ...

  3. Android 内存泄漏分析与解决方法

    Android 内存泄漏分析与解决方法 参考文章: (1)Android 内存泄漏分析与解决方法 (2)https://www.cnblogs.com/start1225/p/6903419.html ...

  4. android 内存泄漏分析工具,Android内存泄漏终极解决篇(上)

    一.概述 在Android的开发中,经常听到"内存泄漏"这个词."内存泄漏"就是一个对象已经不需要再使用了,但是因为其它的对象持有该对象的引用,导致它的内存不能 ...

  5. Android内存泄漏的检测流程、捕捉以及分析

    https://blog.csdn.net/qq_20280683/article/details/77964208 Android内存泄漏的检测流程.捕捉以及分析 简述: 一个APP的性能,重度关乎 ...

  6. Android内存泄漏的简单检查与分析方法

    导语 内存泄漏问题大约是Android开发者最烦恼的问题之一了,项目中连续遇到几个内存泄漏问题,这里简单总结下检查分析内存泄漏的一些工具与方法. 一.什么是内存泄漏? 大家都知道,java是有垃圾回收 ...

  7. 一篇文章搞定《Android内存泄漏》

    ------<Android内存泄漏> 什么是内存泄漏 常见的内存泄漏以及规避方式 单例模式引用Activity 非静态内部类 注册的反注册 定时器Timer WebView的内存泄漏 资 ...

  8. Android内存泄漏总结

    Android 内存泄漏总结 箫鉴哥 2016-01-19 13:44:26 浏览42979 评论10 android 性能优化 阿里技术协会 内存管理 内存泄漏 摘要: Android 内存泄漏总结 ...

  9. Android 内存泄漏分析指北

    android 内存泄漏分析指北 简单来说内存泄漏就是当对象不再被应用程序使用,但是垃圾回收器却不能移除它们,因为它们正在被引用 java 垃圾回收介绍: Java 虚拟机运行所管理的内存包括以下几个 ...

最新文章

  1. mysql数据库验证登陆不上_MySQL数据库连接不上、密码修改问题
  2. 纯干货-多场景下大屏可视化应用(文中含可视化源代码链接)
  3. 【多线程高并发】深入浅出原子性
  4. BugKuCTF 加密 滴答~滴
  5. C语言数组中两个数字之间的最大差的算法(附完整源码)
  6. hadoop环境搭建笔记
  7. Andrew Ng机器学习(一):线性回归
  8. Up or out!!!
  9. Tecplot中使用公式
  10. JavaScript中本地对象、内置对象和宿主对象
  11. opencv画图_c++
  12. Material Design入门(三)
  13. Detectron2和MMDetection的学习笔记
  14. mysql集群之MHA简单搭建
  15. 占用率_有问有答:任务管理器里面的GPU占用率到底是怎么算的?
  16. Window Linux双系统安装 超详细教程
  17. ios7禁止屏幕旋转,强制竖屏
  18. 欧盟 | 地平线 2020 ENSEMBLE:D2.13 SOTIF Safety Concept(上)
  19. 北理计算机优营会被鸽吗,被放鸽子以后~
  20. 内九外七皇城四,九门八点一口钟_ywyuan_新浪博客

热门文章

  1. 北京市优秀毕业论文—基于车辆轨迹时空数据的城市热点预测模型研究
  2. Cesium 热力棱柱分布图
  3. Cozmo 机器人编程环境搭建
  4. 华纳云:香港服务器屏蔽美国IP地址的方式有哪些
  5. Bash'sGame
  6. 2023专升本计算机周考试题四(含答案)
  7. 河南省专升本考试技巧与选择题
  8. Django实现登录、注册、修改密码、重置密码
  9. 七种技巧助你熟练用上IE7.0
  10. 关于程序中的路径问题