Android技术分享|Android 中部分内存泄漏示例及解决方案

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android技术分享|Android 中部分内存泄漏示例及解决方案相关的知识,希望对你有一定的参考价值。

参考技术A

内存泄漏:

举例:

请注意以下的例子是虚构的

内存抖动

源自android文档中的 Memory churn 一词,中文翻译为内存抖动。

指快速频繁的创建对象从而产生的性能问题。

引用Android文档原文:

Java内存泄漏的根本原因是 长生命周期 的对象持有 短生命周期 对象的引用就很可能发生内存泄漏。

尽管短生命周期对象已经不再需要,但因为长生命周期依旧持有它的引用,故不能被回收而导致内存泄漏。

静态集合类引起的内存泄漏


如果仅仅释放引用本身(tO = null), ArrayList 依然在引用该对象,GC无法回收。

监听器

在Java应用中,通常会用到很多监听器,一般通过 addXXXXListener() 实现。但释放对象时通常会忘记删除监听器,从而增加内存泄漏的风险。

各种连接

如数据库连接、网络连接(Socket)和I/O连接。忘记显式调用 close() 方法引起的内存泄漏。

内部类和外部模块的引用

内部类的引用是很容易被遗忘的一种,一旦没有释放可能会导致一系列后续对象无法释放。此外还要小心外部模块不经意的引用,内部类是否提供相应的操作去除外部引用。

单例模式

由于单例的静态特性,使其生命周期与应用的生命周期一样长,一旦使用不恰当极易造成内存泄漏。如果单利持有外部引用,需要注意提供释放方式,否则当外部对象无法被正常回收时,会进而导致内存泄漏。

集合类泄漏

如集合的使用范围超过逻辑代码的范围,需要格外注意删除机制是否完善可靠。比如由静态属性 static 指向的集合。

单利泄漏

以下为简单逻辑代码,只为举例说明内存泄漏问题,不保证单利模式的可靠性。


AppManager 创建时需要传入一个 Context ,这个 Context 的生命周期长短至关重要。

1. 如果传入的是 Application 的 Context ,因为 Application 的生命周期等同于应用的生命周期,所以没有任何问题。

2. 如果传入的是 Activity 的 Context ,则需要考虑这个 Activity 是否在整个生命周期都不会被回收了,如果不是,则会造成内存泄漏。

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


应该将该内部类单独封装为一个单例来使用。

匿名内部类/异步线程


Runnable都使用了匿名内部类,将持有MyActivity的引用。如果任务在Activity销毁前未完成,将导致Activity的内存无法被回收,从而造成内存泄漏。

解决方法:将Runnable独立出来或使用静态内部类,可以避免因持有外部对象导致的内存泄漏。

Handler造成的内存泄漏


Handler属于TLS(Thread Local Storage)变量,生命周期与Activity是不一致的,容易导致持有的对象无法正确被释放

当Android应用程序启动时,该应用程序的主线程会自动创建一个Looper对象和与之关联的MessageQueue。

当主线程中实例化一个Handler对象后,它就会自动与主线程Looper的MessageQueue关联起来。所有发送到MessageQueue的Messag都会持有Handler的引用,所以Looper会据此回调Handle的handleMessage()方法来处理消息。只要MessageQueue中有未处理的Message,Looper就会不断的从中取出并交给Handler处理。

另外,主线程的Looper对象会伴随该应用程序的整个生命周期。

在Java中,非静态内部类和匿名类内部类都会潜在持有它们所属的外部类的引用,但是静态内部类却不会。

当该 Activity 被 finish() 掉时,延迟执行任务的 Message 还会继续存在于主线程中,它持有该 Activity 的 Handler 引用,所以此时 finish() 掉的 Activity 就不会被回收了从而造成内存泄漏(因 Handler 为非静态内部类,它会持有外部类的引用,在这里就是指 SampleActivity)。


避免不必要的静态成员变量

对于BroadcastReceiver、ContentObserver、File、Cursor、Stream、Bitmap等资源的使用,应在Activity销毁前及时关闭或注销。

不使用WebView对象时,应调用`destroy()`方法销毁。

android Activity,Fragment,Application内存状态监听及等级

  @Override
    public void onTrimMemory(int level) 
        super.onTrimMemory(level);
        switch (level)
            case TRIM_MEMORY_COMPLETE://应用程序不可见-内存低-位于LRU底部-80-应用程序可能会被杀死
            case TRIM_MEMORY_MODERATE://应用程序不可见-内存低-位于LRU中部-60-设备当前的运行环境内存较少
            case TRIM_MEMORY_BACKGROUND://应用程序不可见-内存低-位于LRU顶部,但是位置在下降-40--设备当前的运行环境内存较少
            case TRIM_MEMORY_UI_HIDDEN://应用程序不可见-20-程序处于后台应当释放一些内存
            case TRIM_MEMORY_RUNNING_CRITICAL://应用程序可见-15-内存紧张-位于LRU顶部-其他进程可能会销毁以获得更多可用内存
            case TRIM_MEMORY_RUNNING_LOW: //应用程序可见-内存低-10-位于LRU顶部-设备的可用内存越来越少
            case TRIM_MEMORY_RUNNING_MODERATE://应用程序可见-内存较少-5-位于LRU顶部系统即将进入低内存状态
                // TODO 从上到下依次可以处理一些释放内存的操作
                break;
        
    

 

以上是关于Android技术分享|Android 中部分内存泄漏示例及解决方案的主要内容,如果未能解决你的问题,请参考以下文章

Android技术分享| Android 中部分内存泄漏示例及解决方案

android如何改变editText控件中部分文字的格式

修改TextView中部分文本的字体及颜色

修改TextView中部分文本的字体及颜色

android Activity,Fragment,Application内存状态监听及等级

Android技术分享| 视频通话开发流程