android 常见内存泄漏原因及解决办法
android常见内存泄漏主要有以下几类:
一、Handler 引起的内存泄漏。
在Android开发中,我们经常会使用Handler来控制主线程UI程序的界面变化,使用非常简单方便,但是稍不注意,很容易引发内存泄漏。
我们知道,Handler、Message、MessageQueue是相互关联在一起的,Handler通过发送消息Message与主线程进行交互,如果Handler发送的消息Message尚未被处理,该Message及发送它的Handler对象将被MessageQueue一直持有,这样就可能会导致Handler无法被回收。
请看下面的代码:
SecondActivity代码中有一个延迟1秒执行的消息Message,当界面从SecondActivity跳转到ThirdActivity时,SecondActivity自动进入后台,此时如果系统资源紧张(或者打开设置里面的“不保留活动”选项),SecondActivity将会被finish。但问题来了,由于SecondActivity的Handler对象mHandler为非静态匿名内部类对象,它会自动持有外部类SecondActivity的引用,从而导致SecondActivity无法被回收,造成内存泄漏。
解决办法:将Handler声明为静态内部类,就不会持有外部类SecondActivity的引用,其生命周期就和外部类无关,如果Handler里面需要context的话,可以通过弱引用方式引用外部类。参考代码如下:
通过上面的方法,创建一个静态Handler内部类,其持有的对象context使用弱引用,可以避免SecondActivity内存泄漏,但是Looper线程的消息队列中可能还有待处理的消息,所以在Activity的onDestroy方法中,还要记住移除消息队列中待处理的消息。参考代码如下:
二、单例模式引起的内存泄漏
由于单例的生命周期是和app的生命周期一致的,如果使用不当很容易引发内存泄漏。如下代码:
这是一个单例模式的标准写法,表面上看没有任何问题,但是细心的同学会发现,构建该单例的一个实例时需要传入一个Context,此时传入的Context就非常关键,如果此时传入的是Activity,由于Context会被创建的实例一直持有,当Activity进入后台或者开启设置里面的不保留活动时,Activity会被销毁,但是单例持有它的Context引用,Activity又没法销毁,导致了内存泄漏。
如果此时传入的Context是ApplicationContext,由于ApplicationContext的生命周期是和app一致的,不会导致内存泄漏。但是我们不能指望使用这个单例的用户始终传入期望的Context,因此需要对这个单例设计进行调整,可以在构造函数中对mContext赋值改为this.mContext = context.getApplicationContext;当然,也可以直接不让用户传入context。
参考解决办法:
1、一般在我们开发的应用中,都会实现Application,在里面做一些全局性的事情。可以在该实现里面对外提供一个单例,通过此实例来获取ApplicationContext。代码如下:
2、重构Singleton,把构建单例时的context去掉,避免外面使用的人传入错误参数,代码如下:
三、非静态内部类创建静态实例引起的内存泄漏
请看下面的代码:
上述代码中,SecondActivity2包含一个内部类InnerClass,并且在onCreate代码中创建了InnerClass的静态实例mInner,该实例和app的生命周期是一致的。在某些场景,如Activity需要频繁切换,需要不断加载大量图片的场合,是会出现上述代码的,每次Activity启动之后都会使用该单例,避免重复一些有压力的操作。但是这样会引起内存泄漏,因为非静态的内部类InnerClass会自动持有外部类SecondActivity2的引用,创建的静态实例mInner就会一直持有SecondActivity2的引用,导致SecondActivity2需要销毁的时候没法正常销毁。
怎么知道静态实例mInner持有SecondActivity2的引用呢?debug程序之后你会清晰的发现静态实例mInner确实持有外部类SecondActivity2的引用,见下图:
上述代码的正确做法是把内部类InnerClass修改为静态的就可以避免内存泄漏了,因为静态内部类InnerClass不在持有外部类SecondActivity2的引用了。见下图:
当然,也可以把InnerClass单独抽出来作为一个内,写成单例模式,完成同样的功能,同时也可以避免内存泄漏。
四、非静态匿名内部类引起的内存泄漏
在android开发中,相信大家都会不知不觉地用到大量匿名内部类,如接受广播、点击事件、Handler消息处理等等。但是要注意,如果匿名内部类被异步线程使用,可能会引起内存泄漏。请看如下代码:
上述代码中,mRunnable 是非静态匿名内部类,会自动持有外部类SecondActivity3的引用,但是mRunnable被异步线程Thread使用,这样就会导致SecondActivity3在销毁的时候没法正常销毁,从而引起内存泄漏。
正确的做法应该是把mRunnable设置为静态的,这样就不会自动持有外部类SecondActivity3的引用,也就不会引起内存泄漏了。
五、注册/反注册未成对使用引起的内存泄漏
在andorid开发中,我们经常会在Activity的onCreate中注册广播接受器、EventBus等,如果忘记成对的使用反注册,可能会引起内存泄漏。开发过程中应该养成良好的相关,在onCreate或onResume中注册,要记得相应的在onDestroy或onPause中反注册。
六、资源对象没有关闭引起的内存泄漏
在android中,资源性对象比如Cursor、File、Bitmap、视频等,系统都用了一些缓冲技术,在使用这些资源的时候,如果我们确保自己不再使用这些资源了,要及时关闭,否则可能引起内存泄漏。因为有些操作不仅仅只是涉及到Dalvik虚拟机,还涉及到底层C/C++等的内存管理,不能完全寄希望虚拟机帮我们完成内存管理。
在这些资源不使用的时候,记得调用相应的类似close()、destroy()、recycler()、release()等函数,这些函数往往会通过jni调用底层C/C++的相应函数,完成相关的内存释放。
七、集合对象没有及时清理引起的内存泄漏
我们通常会把一些对象装入到集合中,当不使用的时候一定要记得及时清理集合,让相关对象不再被引用。如果集合是static、不断的往里面添加东西、又忘记去清理,肯定会引起内存泄漏。
使用 LeakCanary 检测 Android 的内存泄漏
内存泄漏防不胜防,通过LeakCanary工具,我们能在开发测试阶段发现绝大多数的内存泄漏。这个工具是开源的,使用也非常方便,而且能够相对准确定位出是哪里出现内存泄漏。
下面以AndroidStudio为例介绍LeakCanary的使用:
1、在build.gradle中配置leakcanary的引用
compile 'com.squareup.leakcanary:leakcanary-android:1.4-beta2'
2、使用RefWatcher监测本该被回收的对象。
LeakCanary.install() 会返回一个预定义的 RefWatcher,同时也会启用一个 ActivityRefWatcher,用于自动监控调用 Activity.onDestroy() 之后泄露的 activity。
在自己的Application中添加如下代码:
只需要上述两个步骤之后,LeakCanary就会自动监测内存泄漏,如果有内存泄漏在手机上面会提示,通知栏也会有通知,点击进去之后可以看到具体内存泄露的地方。debug的话,在控制台也会有相关的log输出。
总结:
1、Handler持有的引用最好使用弱引用,在Activity被释放的时候要记得清空Message,取消Handler对象的Runnable;
2、非静态内部类、非静态匿名内部类会自动持有外部类的引用,为避免内存泄露,可以考虑把内部类声明为静态的;
3、对于生命周期比Activity长的对象,要避免直接引用Activity的context,可以考虑使用ApplicationContext;
4、广播接收器、EventBus等的使用过程中,注册/反注册应该成对使用;
5、不再使用的资源对象Cursor、File、Bitmap等要记住正确关闭;
6、集合里面的东西、有加入就应该对应有相应的删除。
android 常见内存泄漏原因及解决办法的更多相关文章
- Android 常见 Memory Leak 原因及解决办法总结
待整理: http://geek.csdn.net/news/detail/50692 背景 在Android开发过程中,我们经常碰到的情况就是在我们不清楚为什么情况下,程序突然出现Crash了.其中 ...
- c++内存泄漏原因及解决办法(智能指针)
内存泄漏 由于疏忽或错误造成程序未能释放已经不再使用的内存的情况.内存泄漏并非指内存在物理上的消失,而是应用程序分配某段内存后,由于设计错误,失去了对该段内存的控制,因而造成了内存的浪费. 内存泄露的 ...
- Android开发 |常见的内存泄漏问题及解决办法
在Android开发中,内存泄漏是比较常见的问题,有过一些Android编程经历的童鞋应该都遇到过,但为什么会出现内存泄漏呢?内存泄漏又有什么影响呢? 在Android程序开发中,当一个对象已经不需要 ...
- 5个Android开发中比较常见的内存泄漏问题及解决办法
android中一个对象已经不需要了,但是其他对象还持有他的引用,导致他不能回收,导致这个对象暂存在内存中,这样内存泄漏就出现了. 内存泄漏出现多了,会是应用占用过多的没存,当占用的内存超过了系统 ...
- .Net内存泄露原因及解决办法
.Net内存泄露原因及解决办法 1. 什么是.Net内存泄露 (1).NET 应用程序中的内存 您大概已经知道,.NET 应用程序中要使用多种类型的内存,包括:堆栈.非托管堆和托管堆.这里我们需 ...
- Android 常见内存泄漏的解决方式
在Android程序开发中.当一个对象已经不须要再使用了,本该被回收时.而另外一个正在使用的对象持有它的引用从而导致它不能被回收.这就导致本该被回收的对象不能被回收而停留在堆内存中,内存泄漏就产生了. ...
- 转:.Net内存泄露原因及解决办法
1. 什么是.Net内存泄露 (1).NET 应用程序中的内存 您大概已经知道,.NET 应用程序中要使用多种类型的内存,包括:堆栈.非托管堆和托管堆.这里我们需要简单回顾一下. 以运行库为目标 ...
- Android开发之漫漫长途 番外篇——内存泄漏分析与解决
该文章是一个系列文章,是本人在Android开发的漫漫长途上的一点感想和记录,我会尽量按照先易后难的顺序进行编写该系列.该系列引用了<Android开发艺术探索>以及<深入理解And ...
- 【原创】android内存管理-内存泄漏原因
转载请注明出处 http://www.cnblogs.com/weiwangnuanyang/p/5704596.html 先讲一下内存泄漏的概念:内存泄露是指无用对象持续占有内存,或者内存得不到及时 ...
随机推荐
- 超分辨率论文CVPR-Kai Zhang
深度学习与传统方法结合的超分辨率:Kai Zhang 1. (CVPR, 2019) Deep Plug-and-Play Super-Resolution for Arbitrary https:/ ...
- java使用jdom生成xml格式文件
本文生成xml使用的工具是jdom.jar,下载地址如下: 链接:https://eyun.baidu.com/s/3slyHgnj 密码:0TXF 生成之后的文档格式类型,就如上面的图片一样,简单吧 ...
- SQLServer转MYSQL的方法(连数据)[传]
转自 https://blog.csdn.net/AlbenXie/article/details/77449720 SQLServer转MYSQL的方法(连数据) 本次转换需要依赖使用工具Navic ...
- HTML开发之(块级标签,行内标签,行内块标签)
显示模式的特性: 主要分为两大类: 块级元素:独占一行,对宽高的属性值生效:如果不给宽度,块级元素就默认为浏览器的宽度,即就是100%宽: 行内元素:可以多个标签存在一行,对宽高属性值不生效,完全靠内 ...
- 在 Linux 使用 GCC 编译C语言共享库
对任何程序员来说库都是必不可少的.所谓的库是指已经编译好的供你使用的代码.它们常常提供一些通用功能,例如链表和二叉树可以用来保存任何数据,或者是一个特定的功能例如一个数据库服务器的接口,就像MySQL ...
- jquery 动态展示查询条件
<table class="queryTable" width="100%" > <tr> <td class="que ...
- mybatis 之 resultType="Map" parameterType="String"
<select id="getAllGoodsForSouJiaYi" resultType="Map" parameterType="Stri ...
- struts1的配置文件详解
要想使用Struts,至少要依靠两个配置文件:web.xml和struts-config.xml.其中web.xml用来安装Struts框架.而struts-config.xml用来配置在Struts ...
- ubuntu下升级特定软件与查看软件版本信息
ubuntu 升级软件: sudo apt-get update 更新源 sudo apt-get upgrade 更新已安装的包 sudo apt-get dist-upgrade 升级系统 ubu ...
- django restframwork 教程之authentication权限
当前我们的API在编辑或者删除的时候没有任何限制,我们不希望有些人有高级的行为,确保: 代码段始终与创建者相关联 只允许授权的用户可以创建代码段 只允许代码段创建者可以更新和删除 没有认证的请求应该有 ...