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)之间的区别。


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

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. 使用静态内部类的方式,静态内部类不默认持有外部类的引用。
  1. private static class MyThread extends Thread {
            @Override
            public void run() {
                //todo someting
            }
        }
    

  2. 当外部类结束生命周期时(即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的引用,导致无法回收。

解决方案:

尽量避免 static 成员变量引用资源耗费过多的实例(如 Context),若需引用 Context,则尽量使用Applicaiton的 Context。
使用弱引用(WeakReference) 代替强引用持有实例。
在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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

安装CUDA与CUDNN与Pytorch(最新超级详细图文版本2023年8月最新)

一、安装CUDA 1.1、下载安装包 cuda可以认为就是Nvidia为了显卡炼丹搞的一个软件&#xff0c;其下载地址为&#xff1a;CUDA Toolkit 12.2 Update 1 Downloads | NVIDIA Developer 当你点进这个链接的时候&#xff0c;你需要依次选择 1是选择系统&#xff0c;这里选windows…

Netty面试题1

计算机网络模型 OSI采用了分层的结构化技术&#xff0c;共分七层&#xff0c; 物理层、数据链路层、网络层、传输层、会话层、表示层、应用层 。 Open System Interconnect 简称OSI&#xff0c;是国际标准化组织(ISO)和国际电报电话咨询委员会(CCITT)联合制定的开放系统互连参…

医疗保健中的 NLP:实体链接

一、说明 HEalthcare和生命科学行业产生大量数据&#xff0c;这些数据是由合规性和监管要求&#xff0c;记录保存&#xff0c;研究论文等驱动的。但随着数据量的增加&#xff0c;搜索用于研究目的的必要文件和文章以及数据结构成为一个更加复杂和耗时的过程。例如&#xff0c;如…

微信小程序中的分包使用介绍

一、分包的好处 可以优化小程序首次启动的下载时间 在多团队共同开发时可以更好的解耦协作 主包&#xff1a;放置默认启动页面/TabBar 页面&#xff0c;公共资源/JS 脚本 分包&#xff1a;根据开发者的配置进行划分 限制&#xff1a;所有分包大小不超过 20M&#xff0c;单…

无人驾驶实战-第十二课(强化学习自动驾驶系统)(完)

在七月算法上报了《无人驾驶实战》课程&#xff0c;老师讲的真好。好记性不如烂笔头&#xff0c;记录一下学习内容。 课程入口&#xff0c;感兴趣的也可以跟着学一下。 ————————————————————————————————————————— 强化学习&#xff…

php webshell 免杀入门

webshell 查杀软件&#xff1a; d盾、安全狗、护卫神、Sangfor WebShellKill 在线查杀 百度WEBDIR https://scanner.baidu.com 河马 https://www.shellpub.com cloudwalker牧云 https://webshellchop.chaitin.cn 查杀技术 静态检测、动态检测、日志检查 静态检查&#xff1a…

通用FIR滤波器的verilog实现(内有Lowpass、Hilbert参数生成示例)

众所周知&#xff0c;Matlab 中的 Filter Designer 可以直接生成 FIR 滤波器的 verilog 代码&#xff0c;可以方便地生成指定阶数、指定滤波器参数的高通、低通、带通滤波器&#xff0c;生成的 verilog 代码也可以指定输入输出信号的类型和位宽。然而其生成的代码实在算不上美观…

智能型静电消除器的优势有哪些?

智能型静电消除器是一种使用先进技术和智能控制系统来消除静电问题的设备。静电是由于电荷不平衡而引起的现象&#xff0c;常见于工业生产、医疗设备、办公环境等场合。静电的存在可能导致电子设备故障、火灾、等问题。 智能型静电消除器与传统静电消除器相比&#xff0c;具有以…

Python做一个绘图系统3:从文本文件导入数据并绘图

文章目录 导入数据文件对话框修改绘图逻辑源代码 Python绘图系统系列&#xff1a;将matplotlib嵌入到tkinter 简单的绘图系统 导入数据 单纯从作图的角度来说&#xff0c;更多情况是已经有了一组数据&#xff0c;然后需要将其绘制。这组数据可能是txt格式的&#xff0c;也可能…

uni-app:实现点击按钮,进行数据累加展示(解决数据过多,导致出错)

效果 代码 核心代码 一、标签显示 <!-- 加载更多 --> <view class"load_more" v-if"info.length > pageNum * pageSize" tap"loadMore">加载更多 </view> v-if"info.length > pageNum * pageSize"&#xf…

远景智能PMO负责人严晓婷受邀为第十二届中国PMO大会演讲嘉宾

上海远景科创智能科技有限公司PMO负责人严晓婷女士受邀为由PMO评论主办的2023第十二届中国PMO大会演讲嘉宾&#xff0c;演讲议题&#xff1a;能源物联网产品标准项目和非标准项目的并行管理。大会将于8月12-13日在北京举办&#xff0c;敬请关注&#xff01; 议题简要&#xff1…

教雅川学缠论07-中枢实战众泰汽车000980

本文实战众泰汽车 下面是2023年11月14-2023年8月8众泰汽车日K图 先画日K 接下来处理包含&#xff0c;就变成下面这个样子 下面在套上缠论的理论&#xff0c;未来股价的走势应该是红色椭圆形虚线里面的样子 好了&#xff0c;文章就到这里&#xff0c;如果众泰最终不是这个走势…

网页版Java(Spring/Spring Boot/Spring MVC)五子棋项目(四)对战模块

网页版Java&#xff08;Spring/Spring Boot/Spring MVC&#xff09;五子棋项目&#xff08;四&#xff09;对战模块 一、约定前后端交互接口1. 建立连接接口2. 针对落子的请求和响应 二、实现前端页面三、实现后端1. 当用户进入房间&#xff0c;更新用户状态 OnlineUserManager…

W5500-EVB-PICO作为TCP Client 进行数据回环测试(五)

前言 上一章我们用W5500-EVB-PICO开发板通过DNS解析www.baidu.com&#xff08;百度域名&#xff09;成功得到其IP地址&#xff0c;那么本章我们将用我们的开发板作为客户端去连接服务器&#xff0c;并做数据回环测试&#xff1a;收到服务器发送的数据&#xff0c;并回传给服务器…

FFmpeg 编码详细流程

介绍 FFmpeg的 libavcodec 模块完成音视频多媒体的编解码模块。FFmpeg 本身不具有音视频编码的功能和底层能力&#xff0c;只是对各类第三方的编码器API 进行封装调用。老版本的 FFmpeg 将avcodec_encode_video2()作为视频的解码函数 API&#xff0c;将avcodec_encode_audio2(…

IO模型-信号驱动IO

linux内核中存在一个信号SIGIO&#xff0c;这个信号就是用于实现信号驱动IO的。当应用程序中想要以信号驱动IO的模型读写硬件数据时&#xff0c;首先注册一个SIGIO信号的信号处理函数,当硬件数据就绪&#xff0c;硬件会发起一个中断&#xff0c;在硬件的中断处理函数中向当前进…

css-4:元素水平垂直居中的方法有哪些?如果元素不定宽高呢?

1、背景 在开发中&#xff0c;经常遇到这个问题&#xff0c;即让某个元素的内容在水平和垂直方向上都居中&#xff0c;内容不仅限于文字&#xff0c;可能是图片或其他元素。 居中是一个非常基础但又是非常重要的应用场景&#xff0c;实现居中的方法存在很多&#xff0c;可以将这…

解决 Android Studio 的 Gradle 面板上只有关于测试的 task 的问题

文章目录 问题描述解决办法 笔者出问题时的运行环境&#xff1a; Android Studio Flamingo | 2022.2.1 Android SDK 33 Gradle 8.0.1 JDK 17 问题描述 笔者最近发现一个奇怪的事情。笔者的 Android Studio 的 Gradle 面板上居然除了用于测试的 task 之外&#xff0c;其它什…

centos8.5本地yum源报错

在下载文件出现以下错误 [rootserver ~]# yum install gcc Updating Subscription Management repositories. Unable to read consumer identity This system is not registered with an entitlement server. You can use subscription-manager to register. RHEL8.5-BaseOS …