Android 内存泄漏

news2024/11/16 9:41:47

名词解释

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

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

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

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

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

1.1非静态内部类

非静态内部类(non static inner class)和 静态内部类(static inner class)之间的区别。
在这里插入图片描述

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

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 实例),导致它的外部类无法被回收。
在这里插入图片描述

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 Studio 中选择 Code->Inspect Code。
在这里插入图片描述
然后会弹出选择检测范围
在这里插入图片描述

点击Ok,等待分析结果
在这里插入图片描述

这个工具除了会检测内存泄漏,还会检测代码是否规范、是否有没用到的导包、可能的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 内存泄露分析

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/637354.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

阿里P8整理的《亿级并发系统设计》实战教程,面面俱到,实在太全了

候&#xff0c;那系统架构一定不是那么简单的&#xff0c;用个 redis&#xff0c;用 mq 就能搞定&#xff1f;当然不是&#xff0c;真实的系统架构搭配上业务之后&#xff0c;会比这种简单的所谓“高并发架构”要复杂很多倍。 如果有面试官问你个问题说&#xff0c;如何设计一…

亚马逊、eBay新品期没有出单怎么办?自养买家号的重要性和技巧

一&#xff1a;新品期没有出单怎么办&#xff1f; 1.刚开始,低bid 调整&#xff0c;20元预算&#xff0c;让位置可以靠后点&#xff0c;因为前期跟前面比&#xff0c;比不赢&#xff0c;不如去后面比。 2.价格不是由卖家单一决定&#xff0c;而是由市场决定的。 3.切记自嗨型…

Kotlin Lambda表达式和匿名函数的组合简直太强了

Kotlin Lambda表达式和匿名函数的组合简直太强了 简介 首先&#xff0c;在 Kotlin 中&#xff0c;函数是“第一公民”&#xff08;First Class Citizen&#xff09;。因此&#xff0c;它们可以被分配为变量的值&#xff0c;作为其他函数的参数传递或者函数的返回值。同样&…

去中心化公链生态是否还有未来

2020年流动性挖矿带来的高收益让 DeFi Summer 点燃了2021年的大牛市。质押借贷、AMM作市、DEX、GameFi以及元宇宙等赛道的相继爆火&#xff0c;让整个行业看到了区块链的未来潜力&#xff0c;公链生态的繁荣也给项目带来了更多的可能性。而当市场进入熊市时&#xff0c;不少公链…

【netty基础】Java NIO三件套

文章目录 一. 缓冲区1&#xff0e;Buffer操作基本API2&#xff0e;Buffer的基本原理2.1. put操作2.2. get操作2.3. clear()回到初始化buffer的值 3&#xff0e;缓冲区的分配4&#xff0e;缓冲区分片5&#xff0e;只读缓冲区6. 直接(direct)缓冲区7. 内存映射 二. 选择器三. 通道…

直接插入排序--C语言(附详细代码)(附图详解)

目录 插入排序法的介绍 什么是插入排序法&#xff1f; 稳定性分析 插入排序基本思想 例子分析 实现代码 运行结果 插入排序法的介绍 什么是插入排序法&#xff1f; 插入排序&#xff0c;一般也被称为直接插入排序。对于少量元素的排序&#xff0c;它是一个有效的算法 。…

Django-3.2-LTS兼容哪些Python版本?支不支持Python3.9.10?

问&#xff1a;请问Python的3.9.10版本兼不兼容Django的3.2版&#xff1f; 答&#xff1a;Python 3.9.10 和 Django 3.2 之间是兼容的。Django 3.2 是一个长期支持&#xff08;LTS&#xff09;版本&#xff0c;它支持 Python 3.6、3.7、3.8 和 3.9。因此&#xff0c;Python 3.9…

母线差动保护(二)

3、大差和小差 接入大差元件的电流为I母、II母所有支路&#xff08;母联除外&#xff09;的电流&#xff0c;目的是为了判断故障是否为母线区内故障&#xff1b;接入小差元件的电流为接入该段母线的所有支路的电流&#xff0c;目的是为了判断故障具体发生在哪一条母线上。 以双…

ifconfig: RX packets 一直为 0

本博客的很多内容都是经验之谈&#xff0c;目的是给遇到类似问题的小伙伴提供一个解决问题的思路&#xff0c;如果试了不行&#xff0c;可以快速跳过&#xff0c;再寻找其他的解决方案。 如题目所言&#xff0c;今天遇到的问题是和网络连通性相关的&#xff0c;就是网络不通&a…

为什么企业推行OEE总是坚持不下去?

OEE很难推行吗&#xff1f; 企业追求高效率和减少浪费变得尤为重要&#xff0c;而在这个过程中&#xff0c;OEE&#xff08;Overall Equipment Efficiency&#xff09;成为了一个非常有用的工具&#xff0c;它可以为企业提供准确的数据&#xff0c;了解生产过程中存在的浪费程…

AttributeError: module ‘numpy‘ has no attribute ‘typeDict‘

问题描述&#xff1a;运行一个网上下载的PyQt5代码&#xff0c;出现了AttributeError: module numpy has no attribute typeDict的错误。具体如下&#xff1a; Traceback (most recent call last):File "F:/PyQt5/Javacr/main.py", line 16, in <module>from …

面向对象的介绍和内存

学习面向对象内容的三条主线 • Java 类及类的成员&#xff1a;&#xff08;重点&#xff09;属性、方法、构造器&#xff1b;&#xff08;熟悉&#xff09;代码块、内部类 • 面向对象的特征&#xff1a;封装、继承、多态、&#xff08;抽象&#xff09; • 其他关键字的使用…

3 个技巧,让你像技术专家一样解决编码问题

「我应该如何提高解决问题的能力&#xff1f;尽管我掌握了 JavaScript&#xff0c;却无法解决实际问题或理解复杂的 JavaScript 代码。」 经常有年轻的开发者朋友问我类似的问题。对开发者来说&#xff0c;解决问题非常重要。编写优秀的代码是一门创造性的艺术&#xff0c;而要…

Linux第二章之基本指令

目录 第一章、基本指令 01. ls 指令 02. pwd命令 03. cd 指令 04. touch指令 05.mkdir指令&#xff08;重要&#xff09; 06.rmdir指令 && rm 指令&#xff08;重要&#xff09; 07.man指令&#xff08;重要&#xff09; 08.cp指令&#xff08;重要&#xff0…

【性能测试系列】JMeter核心技术:分布式压测和参数化

JMeter分布式压测 为什么要做分布式部署? 在上一篇文章中&#xff0c;我们提到了JMeter的线程启动和运行&#xff0c;是会占用系统资源的&#xff0c;一旦需要大并发&#xff0c;而JMeter单机部署配置不够&#xff0c;将会导致JMeter无法在规定时间内启动对应的线程数&#x…

OpenCV项目开发实战--对图像进行非真实感渲染-附Python、C++的代码实现

编写一个过滤器来创建如上所示的风格化/卡通化图像,OpenCV 3 中边缘保留过滤的非常快速的实现。结果与双边过滤非常相似,但速度更快。 用于边缘感知过滤的域变换 它是Eduardo Gastal 和 Manuel Oliveira 在SIGGRAPH 2011 上题为“边缘感知图像和视频处理的域变换”的论文的部…

MM ME21n/Me22n 采购订单创建保存后增强点

有2处可以增强 我们可以在 013 这里 做这个增强 debug可以看到参数 传递到外部系统中 另外一处是

【VMware】VMware17安装实践记录

目录 1、下载地址 2、安装 2.1 更改一下安装路径 3、激活 前言&#xff1a;本博文记录博主自己安装的过程&#xff0c;便于后续自己学习使用 1、下载地址 联系博主 2、安装 2.1 更改一下安装路径 移除更新和加入计划 3、激活 可使用30天版本

ABeam中国2023社招 | ABeam旗下艾宾信息技术开发(上海)热招职位

招聘岗位 SAP SD Consultant (English Speaker) 职位要求 ■ 3年以上SD项目实施或支持经验 ■ 有效的沟通技巧&#xff0c;快速的反应和积极的态度 ■ 能够在压力下工作或面对挑战 ■ 具备ABAP调试和编程能力 ■ 有MM交叉模块知识优先 ■ 良好的英语能力 SAP EWM/MM Co…

易基因:NAR:ChIP-seq等揭示蛋白质酰基化与c-di-GMP协同调控放线菌发育与抗生素合成机制|项目文章

易基因细菌ChIP-seq测序分析结果见刊《Nucleic Acids Research》 大家好&#xff0c;这里是专注表观组学十余年&#xff0c;领跑多组学科研服务的易基因。 2023年06月07日&#xff0c;华东理工大学生物工程学院和生物反应器工程国家重点实验室叶邦策教授和尤迪副教授为共同通…