Android - 常见内存泄露问题盘点

2023-11-08

1、内存泄漏的本质

内存泄漏的本质就是对象引用未释放,当对象被创建时,如果没有被正确释放,那么这些对象就会一直占用内存,直到应用程序退出。例如,当一个Activity被销毁时,如果它还持有其他对象的引用,那么这些对象就无法被垃圾回收器回收,从而导致内存泄漏

当存在内存泄漏时,我们需要通过GCRoot来识别内存泄漏的对象和引用。

GCRoot是垃圾回收机制中的根节点,根节点包括虚拟机栈、本地方法栈、方法区中的类静态属性引用、活动线程等,这些对象被垃圾回收机制视为“活着的对象”,不会被回收。

当垃圾回收机制执行时,它会从GCRoot出发,遍历所有的对象引用,并标记所有活着的对象,未被标记的对象即为垃圾对象,将会被回收。

当存在内存泄漏时,垃圾回收机制无法回收一些已经不再使用的对象,这些对象仍然被引用,形成了一些GCRoot到内存泄漏对象的引用链,这些对象将无法被回收,导致内存泄漏。

通过查找内存泄漏对象和GCRoot之间的引用链,可以定位到内存泄漏的根源,进而解决内存泄漏问题,LeakCancry就是通过这个机制实现的。

一些常见的GCRoot包括:

  • 虚拟机栈(Local Variable)中引用的对象。

  • 方法区中静态属性(Static Variable)引用的对象。

  • JNI 引用的对象。

  • Java 线程(Thread)引用的对象。

  • Java 中的 synchronized 锁持有的对象。

什么情况会造成对象引用未释放呢?简单举几个例子:

  • 匿名内部类造成的内存泄漏:匿名内部类通常会持有外部类的引用,如果外部类的生命周期比匿名内部类长,(更正一下,这里用生命周期不太恰当,当外部类被销毁时,内部类并不会自动销毁,因为内部类并不是外部类的成员变量,它们只是在外部类的作用域内创建的对象,所以内部类的销毁时机和外部类的销毁时机是不同的,所以会不会取决与对应对象是否存在被持有的引用)那么就会导致外部类无法被回收,从而导致内存泄漏。

  • 静态变量持有Activity或Context的引用:如果一个静态变量持有Activity或Context的引用,那么这些Activity或Context就无法被垃圾回收器回收,从而导致内存泄漏。

  • 未关闭的Cursor、Stream或者Bitmap对象:如果程序在使用Cursor、Stream或者Bitmap对象时没有正确关闭这些对象,那么这些对象就会一直占用内存,从而导致内存泄漏。

  • 资源未释放:如果程序在使用系统资源时没有正确释放这些资源,例如未关闭数据库连接、未释放音频资源等,那么这些资源就会一直占用内存,从而导致内存泄漏。

2、静态引用导致的内存泄漏

当一个对象被一个静态变量持有时,即使这个对象已经不再使用,也不会被垃圾回收器回收,这就会导致内存泄漏

public class MySingleton {
    private static MySingleton instance;
    private Context context;

    private MySingleton(Context context) {
        this.context = context;
    }

    public static MySingleton getInstance(Context context) {
        if (instance == null) {
            instance = new MySingleton(context);
        }
        return instance;
    }
}

上面的代码中,MySingleton持有了一个Context对象的引用,而MySingleton是一个静态变量,导致即使这个对象已经不再使用,也不会被垃圾回收器回收。

如果需要使用静态变量,请注意在不需要时将其设置为null,以便及时释放内存。

3. 匿名内部类导致的内存泄漏

匿名内部类会隐式地持有外部类的引用,如果这个匿名内部类被持有了,就会导致外部类无法被垃圾回收。

public class MyActivity extends Activity {
    private Button button;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        button = new Button(this);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // do something
            }
        });
        setContentView(button);
    }
}

匿名内部类OnClickListener持有了外部类MyActivity的引用,如果MyActivity被销毁之前,button没有被清除,就会导致MyActivity无法被垃圾回收。(此处可以将Button 看作是自己定义的一个对象,一般解法是将button对象置为空)

在Activity销毁时,应该将所有持有Activity引用的对象设置为null。

4. Handler引起的内存泄漏

Handler是在Android应用程序中常用的一种线程通信机制,如果Handler被错误地使用,就会导致内存泄漏。

public class MyActivity extends Activity {
    private static final int MSG_WHAT = 1;
    private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MSG_WHAT:
                    // do something
                    break;
                default:
                    super.handleMessage(msg);
            }
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mHandler.sendEmptyMessageDelayed(MSG_WHAT, 1000 * 60 * 5);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        // 在Activity销毁时,应该将Handler的消息队列清空,以避免内存泄漏。
        mHandler.removeCallbacksAndMessages(null);
        }
}

Handler持有了Activity的引用,如果Activity被销毁之前,Handler的消息队列中还有未处理的消息,就会导致Activity无法被垃圾回收。

在Activity销毁时,应该将Handler的消息队列清空,以避免内存泄漏。

5. Bitmap对象导致的内存泄漏

当一个Bitmap对象被创建时,它会占用大量内存,如果不及时释放,就会导致内存泄漏。

public class MyActivity extends Activity {
    private Bitmap mBitmap;

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

        // 加载一张大图
        mBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.big_image);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        // 释放Bitmap对象
        mBitmap.recycle();
        mBitmap = null;
    }
}

当Activity被销毁时,Bitmap对象mBitmap应该被及时释放,否则就会导致内存泄漏。

当使用大量Bitmap对象时,应该及时回收不再使用的对象,避免内存泄漏。另外,可以考虑使用图片加载库来管理Bitmap对象,例如Glide、Picasso等。

6. 资源未关闭导致的内存泄漏

当使用一些系统资源时,例如文件、数据库等,如果不及时关闭,就可能导致内存泄漏。例如:

public void readFile(String filePath) throws IOException {
    FileInputStream fis = null;
    try {
        fis = new FileInputStream(filePath);
        // 读取文件...
    } finally {
        if (fis != null) {
            try {
                fis.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

上面的代码中,如果在读取文件之后没有及时关闭FileInputStream对象,就可能导致内存泄漏。

在使用一些系统资源时,例如文件、数据库等,要及时关闭相关对象,避免内存泄漏。

避免内存泄漏需要在编写代码时时刻注意,及时清理不再使用的对象,确保内存资源得到及时释放。 ,同时,可以使用一些工具来检测内存泄漏问题,例如Android Profiler、LeakCanary等。

7. WebView 内存泄漏

public class MyActivity extends Activity {
    private WebView mWebView;

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

        mWebView = findViewById(R.id.webview);
        mWebView.loadUrl("https://www.example.com");
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        // 释放WebView对象
        if (mWebView != null) {
            mWebView.stopLoading();
            mWebView.clearHistory();
            mWebView.clearCache(true);
            mWebView.loadUrl("about:blank");
            mWebView.onPause();
            mWebView.removeAllViews();
            mWebView.destroy();
            mWebView = null;
        }
    }
}

当Activity销毁时,WebView对象应该被及时释放,否则就可能导致内存泄漏。

在使用WebView时,要及时释放WebView对象,可以在Activity销毁时调用WebView的destroy方法,同时也要清除WebView的历史记录、缓存等内容,以确保释放所有资源。

8. 监测工具

  1. 内存监视工具:Android Studio提供了内存监视工具,可以在开发过程中实时监视应用程序的内存使用情况,帮助开发者及时发现内存泄漏问题。

  2. DDMS:Android SDK中的DDMS工具可以监视Android设备或模拟器的进程和线程,包括内存使用情况、堆栈跟踪等信息,可以用来诊断内存泄漏问题。

  3. MAT:MAT(Memory Analyzer Tool)是一款基于Eclipse的内存分析工具,可以分析应用程序的堆内存使用情况,识别和定位内存泄漏问题。

  4. 腾讯的Matrix,也是非常好的一个开源项目,推荐大家使用

内存泄漏是指程序中的某些对象或资源没有被妥善地释放,从而导致内存占用不断增加,最终可能导致应用程序崩溃或系统运行缓慢等问题。

常见的内存泄漏问题和对应的最佳实践整理如下

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Android - 常见内存泄露问题盘点 的相关文章

随机推荐

  • 火猴之抽奖大转盘(firemonkey)

    活动中往往有抽奖环节 如何使用firemonkey制作一个抽奖的程序呢 效果 思路 1 rectangle line text作为可以转动的转盘和指针以及按钮 2 pie 共 10个作为不同颜色的底 每个startangle和endangl
  • Linux系统离线安装包及其依赖的下载安装

    一 概述 我们在Linux系统下进行项目开发时 经常会出现缺少某些依赖库或者开发包的情况 这时候一般会通过使用apt命令去联网下载 但在某些特殊情况下 例如终端硬件不支持网络连接 周边缺少有线与无线网络 或者需要批量安装程序到很多终端上时
  • Window平台---IPSEC客户端的安装

    1 安装主机证书 参见证书的申请与安装一节 2 从http vpn ebootis de 站点下载 ipsec exe 3 下载windwos2000的ipsec资源工具 http download microsoft com downlo
  • 代码保护软件VMProtect用户手册控制面板“项目”部分都有哪些功能?

    VMProtect是一种很可靠的工具 可以保护应用程序代码免受分析和破解 但只有在应用程序内保护机制正确构建且没有可能破坏整个保护的严重错误的情况下 才能实现最好的效果 下载VMProtect最新试用版 接下来为大家介绍关于VMProtec
  • 移动距离(跳出C++向下取整带来的误区)

    移动举例问题 文章目录 移动举例问题 问题详情 问题分析 跳出误区 代码 问题详情 X星球居民小区的楼房全是一样的 并且按矩阵样式排列 其楼房的编号为 1 2 3 当排满一行时 从下一行相邻的楼往反方向排号 比如 当小区排号宽度为 6时 开
  • perl进程管理

    原文链接 https www jc2182 com perl perl process manager html 进程管理 您可以按照各种要求使用 Perl 来创建新流程 本教程将列出创建和管理Perl流程的一些重要且最常用的方法 您可以使
  • 使用ChatGPT帮助快速读书:《Rise of the Robots: Technology and the Threat of a Jobless Future》

    有了ChatGPT的帮助 读书也快了 英文版的书也可以快速了解其主要内容 不知道这样囫囵吞枣的阅读有没有其它副作用 先读了几本再说 Rise of the Robots Technology and the Threat of a Jobl
  • 【论文笔记】BEIT V2: Masked Image Modeling with Vector-Quantized Visual Tokenizers

    1 介绍 1 1 核心观点 当时的所有的重建目标都是关于低级图像元素的 低估了高级语义 Q 怎么去定义高级和低级语义 1 2 基本流程 VQ KD编码器首先根据可学习码本将输入图像转换为离散令牌 然后 解码器学习重建由教师模型编码的语义特征
  • 前后端获取当前日期

    js直接获取当天时间 标准格式年月日 时分秒 往后推迟时间 则添加 1小时 60 60 1000 new Date new Date 8 3600 1000 toJSON substr 0 19 replace T 后端获取 new Sim
  • git cherry-pick 解决开发分支选错问题

    应用场景 正常开发流程 创建分支并checkout转换为开发分支进行开发 但我在master开发后commit之后意识到了这个问题 重新git pull后并checkout新分支发现代码改动遗失 因为git pull 会把当前分支覆盖 在请
  • Network 【HDU - 3078】【LCA+暴力查询】

    题目链接 你要是真暴力这道题还是要T的 但是 做了剪枝就会过了 我们知道对于LCA每个节点有它自己的深度 在这里 我就将每个节点的深度数组当作了每个节点道最初根节点的距离了 然后 就是剪枝操作饿了 判断是否是可行解的时候用的是dis x d
  • 最新最全的angular4.x、anuglar2、anuglar8入门实战视频教程

    angular4 x视频教程强势来袭 忙碌的工作 不停的充电 好久没遇到这么实用的教程了 跟同行分享一下 写篇文章 放松放松 有好的技术资源的也希望大家多分享 我会关注学习的 angular4 x angular5 x angular8 x
  • 洛谷 P1876 开灯

    题目链接 https www luogu com cn problem P1876 include
  • 图像分析技术大比拼:图像分类、图像识别、目标检测的优缺点分析与算法比较

    计算机视觉是人工智能领域的一个重要分支 它旨在构建能够理解和处理图像 视频等视觉信息的计算机系统 在计算机视觉领域中 图像分类 图像识别和目标检测是三个重要的任务 一 图像分类 图像分类是计算机视觉领域最基础的任务之一 它的目的是将一张图像
  • Linux--高级IO

    高级IO 1 五种IO模型 阻塞IO 在内核将数据准备好之前 系统调用会一直等待 所有的套接字 默认都是阻塞方式 阻塞IO是最常见的IO模型 非阻塞IO 如果内核还未将数据准备好 系统调用仍然会直接返回 并且返回EWOULDBLOCK错误码
  • PyCharm创建virtualenv方法

    Python的版本众多 在加上适用不同版本的Python Package 这导致在同时进行几个项目时 对库的依赖存在很大的问题 这个时候就牵涉到对Python以及依赖库的版本管理 方便进行开发 virtualenv就是用来解决这个问题的 下
  • Kafka 验证部署(单机版)kafka-producer-perf-test.sh 吞吐量测试工具的基本使用

    1 测试topic创建与删除 1 创建一个测试topic 名为test topic 创建3个分区 每个分区分配1个副本 因为是单机kafka 如果是集群的话可以分配多个副本 如果分配的副本数大于broker的数量时 会报错 bin kafk
  • 华为内部面试题库---(14)

    1 关于虚拟地址空间的说法错误的是 A 进程地址空间是用多少分配多少 4G仅仅是最大限额 B 进程的地址空间并不一定对应实际的物理页 C 不同进程的不同虚拟地址可以映射到相同的物理页 D 不同进程的相同虚拟地址不能映射到相同的物理页 解答
  • KNN回归-预测二手车

    KNN是一个典型的分类模型 就时预测类别 例如苹果 香蕉等 预测的结果是 训练集上已经包含的类别 并不会预测出新的类别 二手车价格预测是预测车的价格 是数值 理应按照回归算法来算 怎么用knn来实现回归问题呢 找到最近的K样本之后 我们直接
  • Android - 常见内存泄露问题盘点

    1 内存泄漏的本质 内存泄漏的本质就是对象引用未释放 当对象被创建时 如果没有被正确释放 那么这些对象就会一直占用内存 直到应用程序退出 例如 当一个Activity被销毁时 如果它还持有其他对象的引用 那么这些对象就无法被垃圾回收器回收