「每日一面」内存泄漏与内存溢出详解

先理解两者概念

内存溢出:

内存溢出也就是常说的OOM(Out Of Memory)是指程序在申请内存时,没有足够的内存空间供其使用

内存泄漏:

是指程序在申请内存后,无法及时释放已申请的内存空间。

内存泄漏的影响

  • 内存很宝贵,即使从效率,责任的角度上,我们也应该降低内存的使用,减少内存的浪费。
  • 内存泄漏导致可用内存越来越少,最终导致OOM。
  • 可用内存减少,GC 被触发,虽然 GC 可以帮助我们回收无用内存,但是频繁的触发 GC 也会影响性能,可能造成程序卡顿。

常见产生的场景及解决办法

内存溢出 常见场景:

1.内存中加载的数据量过于庞大

如一次从数据库取出过多数据;Android中加载没有经过处理的特别大的图片

解决策略:数据库分页加载,图片先进行压缩再进行内存加载显示

2.代码中存在死循环或循环产生过多重复的对象实体;

检查代码,保持优质代码习惯

3.启动参数内存值设定的过小; 如启动Eclipse或者Android Studio 直接挂掉可能就是配置文件设置的申请空间内存太小

内存泄漏常见场景及解决策略:

Android中常见的内存泄漏总结起来就是生产周期长的一直对生命周期短的对象保持引用

  1. 非静态内部类
「每日一面」内存泄漏与内存溢出详解

在 Java 中,内部类会隐式的持有外部类的引用。运行的时候RunningThread 会对 InnerThreadActivity 的引用,由于 RunningThread 线程会一直运行下去,我 finish 掉当前 的 Activity 就会导致 InnerThreadActivity 实例发生泄漏。我们可以采用静态内部类的方式来解 除这种内存泄漏的隐患

注意:尽量使用静态内部类来替代内部类,同时避免让长期运行的任务( 线程 )持有 Activity的引用。


2.匿名内部类

「每日一面」内存泄漏与内存溢出详解

在 Java 中,匿名内部类和非静态内部类一样,都会持有外部类的引用。

解决方式和非静态内部类的方法一样,使用静态内部类来代替匿名内部类

3.Handler 内存泄漏

「每日一面」内存泄漏与内存溢出详解

这段代码泄漏的原因也是因为非静态内部类持有了外部类的引用。

我们的 MemoryLeakHandler 因为内部类的关系会持有 HandlerActivity 实例的引用,我们使用 Handler 来发送消息,这个Handler 会被消息中 target 属性引用,这个 Message 会在我们主线程的消息队 列中存活 10 秒钟,在这段时间内,我 finish 掉当前 Activity 就会造成内存泄漏,并且依然会弹出 Toast 尽管我们已经开不见这个 Activity了。

解决方案依然是采用静态内部类来替代非静态内部类,并且使用 WeakReference 来引用 Activity,如果对象只存在弱引用的话,GC 是会回收这部分内存的。

4.单例/静态引用

「每日一面」内存泄漏与内存溢出详解

EventBus,InputMethodManager,我们代码中经常用到单例,以EventBus为例,我们调用之后要使用就需要进行register注册,注册之后就绑定对应的Activity了,这个时候 EventBus 就会持有 Activity 的引用,由于单例是静态的,生命周期和整个 App 生命周期 一致,如果我们不调用 unRegister 方法的话,EventBusActivity 实例就会泄漏。

解决方案:

  • 不要让我们的对象被静态属性所引用,这很容易造成内存泄漏。
  • 一般来说我们在使用注册方法的时候,library 都会提供相对应的解除注册方法,不要忘了调用!如EventBus的unregsiter,bindService和unBindService,registerReceiver 和 unRegisterReceiver等成对出现的



5.Activity Context & Application Context

「每日一面」内存泄漏与内存溢出详解

如果我们要在Activity中调用getVersionCode

CommonHelper.getCommonHelper(this).getVersionCode() 而this传入的是Activity,那么Context 对象的引用被长期持有导致内存泄漏。

处理方案:

使用 Application Context 来代替 Activity Context 即可,Application Context 在整个 App 生命周期内适用。能使用Application的情况就选择 Application的Context。

再添加一下Application,Activity,Service,ContentProvider等中的Context 适用范围

「每日一面」内存泄漏与内存溢出详解

大家注意看到有一些NO上添加了一些数字,其实这些从能力上来说是YES,但是为什么说是NO呢?下面一个一个解释:

数字1:启动Activity在这些类中是可以的,但是需要创建一个新的task。一般情况不推荐。

数字2:在这些类中去layout inflate是合法的,但是会使用系统默认的主题样式,如果你自定义了某些样式可能不会被使用。

数字3:在receiver为null时允许,在4.2或以上的版本中,用于获取黏性广播的当前值。(可以无视)


6.资源未关闭造成的内存泄漏

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


内存泄漏检测工具

  • MAT
    「Memory AnalysisTools」,网上有很多的使用教程,个人感觉使用较繁琐,需要耐心分析和一定的经验才能定位出内存泄漏。
  • LeakCanary,Square 公司开源作品,使用方便,可以直接定位到泄漏的对象,并且给出调用链。
「每日一面」内存泄漏与内存溢出详解


分享到:


相關文章: