Android 内存泄漏

这篇具有很好参考价值的文章主要介绍了Android 内存泄漏。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

名词解释

内存泄漏:即memory leak。是指内存空间使用完毕后无法被释放的现象,虽然Java有垃圾回收机制(GC),但是对于还保持着引用, 该内存不能再被分配使用,逻辑上却已经不会再用到的对象,垃圾回收器不会回收它们。

内存溢出:即out of memory, 当你要求分配的内存超过了系统给你的内存时, 系统就会抛出out of memory的异常(每个Android能用的内存是有限的) 。比如: 当前应用只剩下4M的空间可用, 但你却加载得到一个需要占用5M空间的图片Bitmap对象, 就会抛出溢出的异常

常见内存泄漏场景&解决方案

1.非静态内部类、匿名类()

非静态内部类会持有外部类的引用,如果非静态内部类的实例是静态的,就会长期的维持着外部类的引用,阻止被系统回收。
解决方案是使用静态内部类

1.1非静态内部类

非静态内部类(non static inner class)和 静态内部类(static inner class)之间的区别。
Android 内存泄漏

如果非静态内部类所创建的实例是静态的其生命周期等于应用的生命周期。非静态内部类默认持有外部类的引用而导致外部类无法释放,最终造成内存泄露。即外部类中持有非静态内部类的静态对象。

public class MainActivity extends Activity {
    //非静态内部类的静态实例引用
    public static TestClass testClass = null;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //保证非静态内部类的实例只有1个
        if (testClass == null) {
            testClass = new TestClass();
        }
    }

    // 非静态内部类
    private class TestClass {
        //todo something
    }
}

当 MainActivity 销毁时,因非静态内部类单例的引用,testClass 的生命周期等于应用的生命周期,持有外部类 MainActivity 的引用,故 MainActivity 无法被 GC 回收,从而导致内存泄漏。

解决方案:
将非静态内部类设置为:静态内部类(静态内部类默认不持有外部类的引用)
该内部类抽取出来封装成一个单例
尽量避免非静态内部类所创建的实例是静态的

1.2 多线程相关的匿名内部类和非静态内部类(继承 Thread 类、实现 Runnable 接口、AsyncTask)

当子线程正在处理任务时,如果外部类销毁, 由于子线程实例持有外部类引用,将使得外部类无法被垃圾回收器(GC)回收,从而造成内存泄露。

public class MainActivity extends Activity {
   
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        new MyThread().start();
    }

    private class MyThread extends Thread {
        @Override
        public void run() {
            //todo someting
        }
    }
}

解决方案:

  1. 使用静态内部类的方式,静态内部类不默认持有外部类的引用。
private static class MyThread extends Thread {
        @Override
        public void run() {
            //todo someting
        }
    }
  1. 当外部类结束生命周期时(即Activity或Fragment),强制结束线程(onDestroy或onDestroyView)。使得工作线程实例的生命周期与外部类的生命周期同步。
    @Override
    protected void onDestroy() {
        super.onDestroy();
        myThread.interrupt();
    }

2. Handler内存泄漏(重新理解为什么 Handler 可能导致内存泄露?)

Handler内部message是被存储在MessageQueue中的,MessageQueue中的 Message 持有 Handler 实例的引用,有些message不能马上被处理,存在的时间会很长,导致handler无法被回收,如果handler是非静态的(内部类、匿名内部类),默认持有外部类的引用(如 MainActivity 实例),导致它的外部类无法被回收。
Android 内存泄漏

public class MainActivity extends Activity {
    private MyHandler myHandler;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        myHandler = new MyHandler();
        new Thread() {
            @Override
            public void run() {
                try {
                    //执行耗时操作
                    Thread.sleep(20000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                //发送消息
                myHandler.sendEmptyMessage(1);
            }
        }.start();
    }

    private class MyHandler extends Handler {

        @Override
        public void handleMessage(Message msg) {
            //处理消息事件
        }
    }
}

解决方案:

  1. 使用静态内部类+弱引用的方式,保证外部类能被回收。因为弱引用的对象拥有短暂的生命周期,在垃圾回收器线程扫描时,一旦发现了具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。
public class MainActivity extends Activity {
    private MyHandler myHandler;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        myHandler = new MyHandler(this);
        new Thread() {
            @Override
            public void run() {
                try {
                    //执行耗时操作
                    Thread.sleep(10000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                //发送消息
                myHandler.sendEmptyMessage(1);
            }
        }.start();
    }

    public void test() {
        Log.d("TAG", "test");
    }

    private static class MyHandler extends Handler {
        //定义弱引用实例
        private WeakReference<Activity> reference;

        //在构造方法中传入需持有的Activity实例
        public MyHandler(Activity activity) {
            //使用 WeakReference 弱引用持有 Activity 实例
            reference = new WeakReference<Activity>(activity);
        }

        @Override
        public void handleMessage(Message msg) {
            //处理消息事件
            //调用Activity实例中的方法
            ((MainActivity) reference.get()).test();
        }
    }
}

  1. 当外部类结束生命周期时,清空 Handler 内消息队列。
    @Override
    protected void onDestroy() {
        if (myHandler!= null) {
            myHandler.removeCallbacksAndMessages(null);
        }
        super.onDestroy();
    }

3. Context导致内存泄漏

根据场景确定使用Activity的Context还是Application的Context,因为二者生命周期不同,对于不必须使用Activity的Context的场景(Dialog),一律采用Application的Context,单例模式是最常见的发生此泄漏的场景,比如传入一个Activity的Context被静态类引用,导致无法回收

4. 静态View导致泄漏

使用静态View可以避免每次启动Activity都去读取并渲染View,但是静态View会持有Activity的引用,导致无法回收。

解决方案:

  1. 尽量避免 static 成员变量引用资源耗费过多的实例(如 Context),若需引用 Context,则尽量使用Applicaiton的 Context。
  2. 使用弱引用(WeakReference) 代替强引用持有实例。
  3. 在Activity销毁的时候将静态View设置为null

5.资源对象未关闭导致

对于资源若在 Activity 销毁时无及时关闭 / 注销这些资源,则这些资源将不会被回收,从而造成内存泄漏。
如广播、文件、Bitmap、数据库等使用

//对于广播BroadcastReceiver:注销注册
unregisterReceiver(broadcastReceiver);

//对于文件流File:关闭流
inputStream / outputStream.close();

//对于数据库游标cursor:使用后关闭游标
cursor.close();

//对于图片资源Bitmap:Android分配给图片的内存只有8M,若1个Bitmap对象占内存较多,当它不再被使用时,应调用recycle()回收此对象的像素所占用的内存;最后再赋为null 
bitmap.recycle();
bitmap = null;

// 对于动画(属性动画),将动画设置成无限循环播放setRepeatCount(ValueAnimator.INFINITE);后
// 在Activity退出时记得停止动画
animator.cancel();

6.监听器未关闭

很多需要register和unregister的系统服务要在合适的时候进行unregister,手动添加的listener也需要及时移除

7.集合中的对象未清理

集合用于保存对象,如果集合越来越大,不进行合理的清理,

8. WebView导致的内存泄漏(目前没有遇到)

WebView只要使用一次,内存就不会被释放,所以WebView都存在内存泄漏的问题。
通常的解决办法是为WebView单开一个进程,使用AIDL进行通信,根据业务需求在合适的时机释放掉

内存泄漏分析工具

lint

lint 是一个静态代码分析工具,同样也可以用来检测部分会出现内存泄露的代码,平时编程注意 lint 提示的各种黄色警告即可。如:
Android 内存泄漏
也可以手动检测,在 Android Studio 中选择 Code->Inspect Code。
Android 内存泄漏
然后会弹出选择检测范围
Android 内存泄漏

点击Ok,等待分析结果
Android 内存泄漏

这个工具除了会检测内存泄漏,还会检测代码是否规范、是否有没用到的导包、可能的bug、安全问题等等。

Memory Profile

Memory Profile 的使用

LeakCanary

LeakCanary

链接

Android内存优化(三)避免可控的内存泄漏
Android应用开发中对Bitmap的内存优化
Android内存优化(五)详解内存分析工具MAT
Android 中内存泄漏的原因分析及解决方案
手把手教你在Android Studio 3.0上分析内存泄漏
RxJava这么好用却容易内存泄漏?解决办法是…
搞定 Android App 的内存泄漏问题
再见,内存泄漏!
关于LiveData可能引发的内存泄漏及优化
LeakCanary纠察内存泄漏后,为什么还是OOM?
Android内存优化(六)LeakCanary使用详解
查内存泄漏神器,LeakCanray原来是这样工作的
LeakCanary源码浅析,内存泄漏检测的好帮手
被问到:如何检测线上内存泄漏,通过 LeakCanary 探究!
Android 内存泄漏检测工具 LeakCanary 的使用
Android 内存泄露分析文章来源地址https://www.toymoban.com/news/detail-480230.html

到了这里,关于Android 内存泄漏的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • STM32电源名词解释

    STM32电源架构 常用名词 VCC C=circuit 表示电路,即接入电路的电压。 VDD D=device 表示器件, 即器件内部的工作电压。 VSS S=series 表示公共连接,通常指电路公共接地端电压。 VDDA A=analog 表示模拟,是模拟电路部分的电源。主要为ADC模块以及其他的模拟电路部分(复位电路、PLL等

    2024年02月11日
    浏览(50)
  • 区块链基本概念和名词解释

    区块链基本概念和名词解释 P2P 共识算法 梅克尔-帕特里夏树 从零开始搭建区块链 至今(2022)从业已经10年了,作为一个IT老鸟,见证了移动互联时代的崛起,甚至参与其中充当一颗光荣的螺丝钉。其间各种各样的所谓新技术、新框架、新工具层出不穷,有的昙花一现,有的

    2024年01月17日
    浏览(62)
  • 智能小程序相关名词解释(汇总)

    小程序 ID 小程序 ID 是智能小程序分配给开发者的应用 ID,是应用的唯一标示,只有应用创建后才可以获取。创建小程序应用后,您可获得小程序应用的小程序 ID。 小程序框架 小程序提供一套简单高效的开发框架,帮助您开发具有原生 App 体验的服务。 整个小程序框架系统分

    2024年01月18日
    浏览(49)
  • 常见通信名词的解释

    在通信接口的介绍中,难免见到全双工/半双工/单工、同步/异步等这些名词。今天就专门来介绍一下这些名词。 数据通信中,数据在线路上的传送方式可以分为单工通信、半双工通信和全双工通信三种。 单工通信:是指消息只能单方向传输的工作方式。例如遥控、遥测(某

    2024年02月06日
    浏览(52)
  • 电气电工相关专业知识及名词解释

    一、电流电压 火线、零线、地线 :火线和零线的区别就是:火线带电,零线不带电。火线是传电流的,而零线是回流的。 红色是火线,零线一般是绿色的,通常可用电笔来测。电笔一头亮了是火线,不亮的则是零线。也可用电压表来测,火线之间的电压是220V,而零线是没有

    2024年02月02日
    浏览(44)
  • 计算机视觉——期末复习(填空、名词解释)

    图像文件: 指包含图像数据的文件,文件内除图像数据本身以外,还有对图像的描述信息等 距离变换: 特殊的变换,把二值图像变换为灰度图像 距离图: 如果考虑目标区域中的每个点与最接近的区域外的点之间的距离, 并用与距离成正比的灰度表示该点的灰度,那么这样

    2024年02月11日
    浏览(46)
  • EDA、PLD、FPGA等名词解释

    加*为常考: *EDA:(electronic design automation)电子设计自动化 *HDL:(hard description language)硬件描述语言 ASIC:(application specific intergrated circuit)专用集成电路 *FPGA:(field programmable gate array)现场可编程逻辑门阵列 *PLD:(programmable logic device)可,编程逻辑器件  *CPLD:(complex pr

    2024年02月06日
    浏览(61)
  • 50个渗透(黑客)常用名词及解释

    目录 前言 一.渗透测试 二.网络安全 三.安全攻击 四.黑客工具 五.渗透方法 六.网络钓鱼 七.攻击技术 八.其他名词 总结 🌈嗨!我是Filotimo__🌈。很高兴与大家相识,希望我的博客能对你有所帮助。 💡本文由Filotimo__✍️原创,首发于CSDN📚。 📣如需转载,请事先与我联系以

    2024年02月09日
    浏览(49)
  • 50个渗透(黑客)常用名词及解释

    目录 前言 一.渗透测试 二.网络安全 三.安全攻击 四.黑客工具 五.渗透方法 六.网络钓鱼 七.攻击技术 八.其他名词 总结 🌈嗨!我是Filotimo__🌈。很高兴与大家相识,希望我的博客能对你有所帮助。 💡本文由Filotimo__✍️原创,首发于CSDN📚。 📣如需转载,请事先与我联系以

    2024年02月06日
    浏览(48)
  • 人工智能发展历史与常见名词解释

    Artificial Intelligence,也叫 AI,这是一个比较统称的说法,通俗来说就是让机器能像人一样对事物做出反应,该领域的研究包括机器人、图像识别(CV)、自然语言处理(NLP)、数据处理(BI)和专家系统等。 按照人工智能的发展程度,行业一般将其分为三个层次: 计算智能:

    2024年02月10日
    浏览(63)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包