Android常见的内存泄漏出现原因的分析及检查办法

什么是内存泄漏?

内存泄漏(Memory Leak)是指程序中己动态分配的堆内存由于某种原因程序未释放或无法释放,造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃等严重后果。

很多人会把内存泄漏和内存溢出混淆,其实两者并不是同一个概念,但是两者却有非常重要的联系,简单来说大量的内存泄漏就会导致内存溢出。下面贴出内存溢出的概念。

内存溢出:程序向系统申请的内存空间超出了系统能给的。

比如内存只能分配一个int类型,我却要塞给他一个long类型,系统就出现oom。简单来说就是房子就那么大,但是越来越多的人进来,直到房子已经装不下这么多人,人还在往房子里面走,就会导致房子越来越拥挤,直到将房子挤爆。

我们理解了内存泄漏和内存溢出的区别之后。那么我们就来看看导致我们程序内存泄漏的根本原因是什么。


暗中观察一番之后,我们来看内存泄漏出现的根本原因。首先我们都知道java是有垃圾回收机制的。也就是我们常说的gc。gc是可以自动清除堆中我们不再使用的对象的。当然了,在java中对象是通过引用来使用的。但是如果再也没有引用指向对象的话,那么这个对象就无从处理,无从调用。在java中我们称这种对象为不可到达对象。简单来说,此对象在内存中的申请的空间我们无法回收,有对象的强引用,且没有及时释放,进而造成内存单元一直被占用,浪费空间,就可能造成内存溢出!

下面我们总结一下安卓内存泄漏出现的原因,常见内存泄漏的汇总。


1.非静态内部类或者匿名内部类隐式持有外部类对象。简单来说当非静态内部类的对象的生命周期比外部类对象生命周期长,就会引起内存泄漏。安卓比较典型场景就是使用handler.

也就是说当handler正在处理消息的时候,用户退出activity,但是这个时候handler还在处理消息。导致activity无法被回收。也就会发生内存泄漏。

解决办法:

1)将handler使用static修饰

2)handler通过弱引用的方式持有activity

3)在activity的ondestory生命周期中将handler中的消息置空

2.单例模式也会引起内存泄漏。我们使用单例模式是希望全局只有一个静态变量,如果我们传入了上下文的话,activity是间接继承上下文的。所以这个时候我们要是将activity退出,应该是回收activity的,但是单例模式还持有着它的引用,导致activity回收失败,造成内存泄漏。

解决办法:

1)不管外面传入什么上下文,我们单例模式里面都给它转化为application的context,这样单例模式的生命周期就和应用一样长,避免了内存泄漏。

3.mvp框架引起内存泄漏。Mvp框架优点很多,包括高度解耦,代码复读性强等等,但是它也有缺点。缺点之一就是容易造成内存泄漏。Presenter层持有着view的接口对象,model也很有可能拥有者presenter实例。所以当activity销毁的时候,model还在获取着数据。Presenter也就一直持有着view对象。这条gc链不间断,activity就无法正常的回收。

解决办法:

1)在actity的ondestory方法中利用presenter层进行资源释放,解除和view层的绑定,并且取消model层的网络请求。最后置空presenter层。

2)将presenter层转化为弱引用去引用view对象

4.RxJava也会引起内存泄漏。内存泄漏产生的根本原因,当一个对象处于可以被回收状态时,却因为该对象被其他暂时不可被回收的对象持有引用,而导致不能被回收,如此一来,该对象所占用的内存被回收以作他用,这部分内存就算是被泄露掉了。简单来说,就是该丢掉的垃圾还占着有用的空间没有被及时丢掉。

解决办法:

1)使用取消订阅管理器,compositeSubscription.让订阅管理器统一管理持有所有请求,统一取消。

2)使用Rxlifecycle第三方库,完成发布事件与当前组件进行绑定,实现生命周期同步,组件生命周期结束后,自动取消订阅。

3) 自己取消订阅,调用unsubscribe()方法

5.timer和timertask(属性动画)引起的内存泄漏,因为我们通常会用来做一些计时操作或者循环操作,如果忘记销毁变量的话,那么timer或者timertask可能会一直持有着activity或者其他变量,造成内存泄漏。属性动画和上述的问题是一样的,所以在这里就集中地说了。

解决办法:

1)在适当的时机调用cancel()方法(比如在activity的ondestory方法里面调用cancel方法)

6.关于webview的内存泄漏,是因为webview加载网页后长期占用内存而不能释放,也就是说webview持有着acitivity变量,导致占用的内存始终无法释放,就算是调用了webview.ondestory()也不能解决问题。

解决方法:

1)当然了,也有最终的解决方案。在webview销毁之前需要先从父容器中将webview移除。然后在调用webview的销毁方法。

Android中检查内存泄漏的方法有很多种,我们这里就介绍平时我们最常用的方法。

1.利用Android Studio自带工具进行内存泄漏的检测。首先我们先打开Android Studio的控制台(logcat),然后找到monitors,打开。我们就可以看到下面这张图这样。

       当我们连接模拟器运行项目的时候,我们就可以通过自带工具看到我们的内存使用情况,当然还有其他的一些功能,(cpu的消耗情况,网络测速,Gpu的绘制情况)。我们都可以在这里看到。如果发生内存泄漏或者内存溢出的话我们就可以发现,但是这种方法不全面,必须要我们关注它的内存消耗状况。不够方便,我们需要的是如果有内存泄漏的话,能够第一时间的通知我们去解决,并且将发生内存泄漏的位置告诉我们。如果这样的话,这种方式就不能满足我们的需求了。这个时候我们就需要另外一种方法了。

2.利用Leakcanary来检查我们项目中出现的内存泄漏。

leakcanary是square公司出的一个第三方检查内存泄漏的工具,在这个工具出现之前,square公司的技术人员也被内存泄漏的问题困扰了很久,当时他们想要利用一种思路,一种方法,彻底解决内存泄漏的问题。但是后来失败了。他们发现他们距离解决问题的方向更遥远了。后来及时的调整思路,这才有了leakcanary。所以说并不是大公司的技术人员不会被这些问题困扰,人和人都是一样的。区分的只是人与人的耐心程度。

下面来介绍一下这个工具具体是怎么使用的。因为是第三方工具,所以我们需要导入两个依赖。一个是debug,一个是release

debugCompile 'com.squareup.leakcanary:leakcanary-android:1.3'

releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.3'

通常来说,我们需要使用这个工具能够检测整个项目中的内存泄漏问题,所以我还是建议抽取一个app类,在这个类中的oncreate方法中获得检测对象RefWatcher。

refWatcher= LeakCanary.install(this);

然后通过application类传递出去。具体方法如下:

public static RefWatcher getRegwatcher(Context context){

MyApp myApp = (MyApp) context.getApplicationContext();

return myApp.refWatcher;

}

然后我们就可以使用了,通过两行代码的调用,我们就可以实现实时的检测我们项目中是否出现了内存泄漏。如果我们想要在MainActivity中检测内存泄漏,我们应该具体怎么写呢?具体代码如下:

RefWatcher regwatcher = MyApp.getRegwatcher(this.getApplicationContext());

regwatcher.watch(this);

通过获得到检测对象RefWatcher,将我们需要检测的对象传递给它的watch()方法就可以了。

当然还有其他检测方法,比如MAT等等,具体使用什么方法还是要看个人本身或者项目中的实际需求,不可盲目使用。

到这里关于Android的内存泄漏常见的原因和检测方法就讲述结束了,关于内存泄漏其实还有很多我们还未了解到的知识,这些书本是不会交给我们的,我们需要实际的去体验,在项目中碰到,我们才能够对内存泄漏有更精进的了解,当然如果项目中没有任何内存泄漏,那肯定是天大的好事。以上我说的如果不对的地方,欢迎各位提出宝贵的意见,一起交流,共同进步。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,189评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,577评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,857评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,703评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,705评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,620评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,995评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,656评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,898评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,639评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,720评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,395评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,982评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,953评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,195评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,907评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,472评论 2 342

推荐阅读更多精彩内容