Android性能优化系列——内存优化

news2024/11/24 14:29:40

内存,是Android应用的生命线,一旦在内存上出现问题,轻者内存泄漏造成App卡顿,重者直接crash,因此一个应用保持健壮,要做好内存的使用和优化。网上有很多讲JAVA内存虚拟机的好文章,我就不赘述了。今天主要总结下内存优化。

作为一个开发者,需要在平时的代码中就要多注意,如有不合理的地方,则需要进行优化。

一、内存泄漏(memory leak):

内存泄漏就是在当前应用周期内不再使用的对象被GC Roots引用,导致不能回收,使实际可使用内存变小,通俗点讲,就是无法回收无用对象。

二、内存溢出(out of memory,也叫OOM):

系统在申请内存空间时,没有总够的内存空间供其使用。简单来说就是系统不能再分配你所需要的内存空间,比如你申请需要100M的内存空间,但是系统仅剩90M了,这时候就会内存溢出。(溢出会导致应用Crash崩溃)

内存泄漏的本质原因:

本该被回收的对象没有被回,继续停留着内存空间中,导致内存被占用。其实是持有引用者的生命周期 > 被持有引用者的生命周期。过多内存泄漏会把内存空间占用完,最终会导致内存溢出。

三、内存泄漏的常见情况

1.非静态内部类创建静态实例

非静态内部类创建静态实例会导致内存泄漏,通常项目中,通常在Activity启动的时候创建单例数据,避免重复创建相同的数据,会在Activity内部创建一个非静态内部类的静态实例。

错误的例子:非静态内部类默认持有外部引用

public class NotStaticActivityextends AppCompatActivity {
    //非静态内部类实例引用,设置为静态
    private static InnerClass sInnerClass;
 
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
 
        //保证非静态内部类实例只有一个
        if (sInnerClass == null) {
            sInnerClass = new InnerClass();
        }
    }
 
    //非静态内部类
    private class InnerClass {
        //……
    }
}

非静态外部类(InnerClass)的生命周期 = 应用的生命周期,而且持有外部类Activity的引用,在Activity在销毁的时候无法被GC回收,从而导致内存泄漏。

优化后的代码1:将非静态内部类设置为静态内部类(静态内部类默认不持有外部类的引用)

  //静态内部类
    private static class InnerClass2 {
        //……
    }

优化后的代码2:将外部类抽取出来封装成一个单例

public class InnerClass  extends AppCompatActivity {
    private static final String TAG = InnerClass3.class.getSimpleName();
    private static InnerClass sInnerClass;
    //单例
    public InnerClass getInstance() {
        if (sInnerClass == null) {
            sInnerClass = new InnerClass();
        }
        return sInnerClass;
    }
}
2.注册对象未注销或资源对象未关闭

注册了像BraodcastReceiver,EventBus这种,没有在页面销毁时注销的话,会引发泄露问题,所以应该在Activity销毁时及时注销。

3.多线程造成内存泄漏

我们知道线程类属性非静态(匿名)内部类,多线程的使用主要是AsyncTask、实现Runnable接口,继承Thread类,在使用线程类时需要注意内存泄漏。

1).AsyncTask

我们常常使用AsyncTask来执行一些异步的耗时操作。

我们需要在界面销毁的时候调用AsyncTask.cancel(true)方法。

@Override
    protected void onDestroy() {
       //强制退出AsyncTask
        mAsyncTask.cancel(true);
        super.onDestroy();
    }

2).Thread和Handler的使用

Thread:线程类Thread属于非静态内部类,它的生命周期比ThreadActivity的生命周期长,运行时默认持有外部类的引用,如果ThreadActivity需要销毁时,线程类Thread持有ThreadActivity的引用,导致ThreadActivity无法回收利用,造成内存泄漏。

我们经常这样使用:


 
    //方式一:新建内部类
        new MyThread().start();
 
    //方式二:匿名Thread内部类
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(1000);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }
 
    //自定义Thread
    private class MyThread extends Thread {
        @Override
        public void run() {
            try {
                Thread.sleep(1000);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

优化方法1;把 private class MyThread extends Thread改成静态内部类  private static class MyThread extends Thread;

优化方法2:在界面的ondestroy方法中,把线程结束掉.

Handler:也是一个内部类,我们在使用的时候也会提示他如果是非静态的,就会造成内存泄漏。

解决方法1:

  如果 Handler 不需要外部 Activity 的数据,直接使用使用静态内部类或外部类即可,避免使用成员内部类。如果需要,还可以使用外部类+弱引用的方法。

private static class MyHandler extends Handler {
     private WeakReference<MainActivity> ref;

     public MyHandler(Activity activity) {
         this.ref = new WeakReference<>(activity);
     }

     @Override
     public void handleMessage(@NonNull Message msg) {
         // TODO:ref.get()...
         
         super.handleMessage(msg);
     }
}

tips:使用成员内部类 + 弱引用的方法是不行的,内部类默认持有外部类引用的。

解决方法2:当外部类结束生命周期时,清空Handler内消息队列;

   @Override
    protected void onDestroy() {
        //当外部类结束生命周期时,清空Handler内消息队列;
        mHandler.removeCallbacksAndMessages(null);
        super.onDestroy();
    }
4.static关键字修饰的成员变量

我们知道被static修饰的成员变量的生命周期等于app应用程序的生命周期。

所以,是否有些不需要的是否可以去掉,静态的改成非静态的。

如果静态的对象引用了activity,是否可以替换成App的上下文就足够了。

5.引用的对象是否回收,置空

(1)动画相关资源使用后,记得及时回收;

(2)IO流相关类:在使用IO流,File文件类或者Sqlite、Cursor等资源时要及时关闭,这些资源在读写操作时一般都行了缓冲,如果不及时关闭,这些缓冲对象就会一直被占用得不到释放,以致内存泄漏,所以在在它们不需要使用时,及时关闭,缓冲释放资源,避免内存泄漏。

  1. stream.close();

  2. cursor.close();

  3. bitmap.recycle();bitmap = null;

  4. arrayList.clear();arrayList = null;

6、内存抖动

当大量的对象创建又回收时,就会内存抖动,例如常见的在onDraw方法中创建对象,应把对象放在类中或者初始化方法中。

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

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

相关文章

嵌入式-C语言-江科大-指针的详解与应用

文章目录 一&#xff1a;计算机存储机制二&#xff1a;定义指针三&#xff1a;指针的操作四&#xff1a;数组与指针五&#xff1a;指针的应用道友&#xff1a;最清晰的脚印&#xff0c;踩在最泥泞的道路上。 推荐视频配合我的笔记使用 [C语言] 指针的详解与应用-理论结合实践&a…

2024年了,难道还不会使用谷歌DevTools么?

我相信您一定对Chrome浏览器非常熟悉,因为它是前端开发者最亲密的伙伴。我们可以使用它查看网络请求、分析网页性能以及调试最新的JavaScript功能。 除此之外,它还提供了许多功能强大但不常见的功能,这些功能可以大大提高我们的开发效率。 让我们来看看。 1. 重新发送XHR…

网络流量分析与故障分析

1.网络流量实时分析 网络监控 也snmp协议 交换机和服务器打开 snmp就ok了 MRTG或者是prgt 用于对网络流量进行实时监测&#xff0c;可以及时了解服务器和交换机的流量&#xff0c;防止因流量过大而导致服务器瘫痪或网络拥塞。 原理 通过snmp监控 是一个…

pyside6 界面美化库的使用

使用qt_material库&#xff0c;在库中进行导入后&#xff0c;直接使用库提供的皮肤即可非常简单 example&#xff1a; # -*- coding: utf-8 -*- # 使用例子 import sys # from PySide6 import QtWidgets # from PySide2 import QtWidgets from PySide6 import QtWidgets from…

CreateDIBSection失败的问题记录

错误记录 [ERROR] (:0, ): QPixmap::fromWinHICON(), failed to GetIconInfo() (操作成功完成。) [ERROR] (:0, ): QPixmap::fromWinHICON(), failed to GetIconInfo() (参数错误。) [ERROR] (:0, ): QPixmap::fromWinHICON(), failed to GetIconInfo() (参数错误。) [ERROR] …

安卓上使用免费的地图OpenStreetMap

前一段使用了微信的地图&#xff0c;非常的好用。但是存在的问题是海外无法使用&#xff0c;出国就不能用了&#xff1b; 其实国内三家&#xff1a;百度&#xff0c;高德&#xff0c;微信都是一样的问题&#xff0c;当涉及到商业使用的时候需要付费&#xff1b; 国外除了谷歌…

每天刷两道题——第十天

1.1和为k的子数组 给你一个整数数组 n u m s nums nums 和一个整数 k k k &#xff0c;请你统计并返回 该数组中和为 k k k 的子数组的个数 。子数组是数组中元素的连续非空序列。 输入&#xff1a;nums [1,2,3], k 3 输出&#xff1a;2 前缀和 1.2如何使用 前缀和的…

【Linux Shell】1. 简述

文章目录 【 1. Shell 解释器、Shell语言、Shell脚本 】【 2. Shell 环境 】【 3. 一个简单的 Shell 脚本 】3.1 Shell 脚本的编写3.2 Shell 脚本的运行3.2.1 作为可执行程序运行 Shell 脚本3.2.2 作为解释器参数运行 Shell 脚本 【 1. Shell 解释器、Shell语言、Shell脚本 】 …

基于WIFI指纹的室内定位算法matlab仿真

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 4.1WIFI指纹定位原理 4.2 指纹数据库建立 4.3定位 5.算法完整程序工程 1.算法运行效果图预览 2.算法运行软件版本 matlab2022a 3.部分核心程序 .....................................…

开发小技巧 - 合理使用Visual Studio 2022内置任务列表(TODO)

前言 在开发编码过程中经常会因为各种问题而打断自己的思绪和开发计划&#xff0c;可能会导致本来准备开发或者需要测试的功能到要上线的时候才想起来没有做完。这种情况相信很多同学都遇到过&#xff0c;咱们强大的Visual Studio内置了一个任务列表&#xff08;TODO&#xff…

使用使用maven后jstl标签库无法使用

创建maven项目后配置了jstl标签库的依赖&#xff0c;但是一直不行&#xff0c;jsp页面还是原样给我输出&#xff0c;然后去网上找了许多办法&#xff0c;类似于配置文件之类的&#xff0c;结果发现对我并没有什么用&#xff0c;还是原样输出 然后就各种查找&#xff0c;发现了一…

rosbag 源码阅读笔记-1

这篇文字想通过在自己的机器上查找rosbag的源码在哪里&#xff08;而不是通过google搜索&#xff09;&#xff0c;来和大家分享一些ros和python的常用命令&#xff0c;了解一下rosbag的调用过程。 怎么查到源码在哪里 当然我们可以直接上ros的官网去查看&#xff0c;路径在这…

Tmux 使用小记

本文参考自 阮一峰老师Tmux 使用教程[1] Tmux,不仅仅是分屏那么简单。。。 与tmux类似的工具是screen 会话管理 将窗口与会话"解绑" 对于没有图形界面只有shell的场景(如服务器)&#xff0c;尤其有用..这是其最核心解决的问题(窗口管理啥的只能算锦上添花的辅助功能)…

1.7~1.8数算总复习PART1

偶数结点就一定是左孩子&#xff0c;奇数结点就一定是右孩子&#xff0c;判断两相邻编号的两节点是否为兄弟&#xff0c;就看他们除以2后的结果是否相同即可 由于先序遍历是“根――左子树――右子树”&#xff0c;而后序遍历是“左子树――右子树――根”&#xff0c;若某二叉…

【OpenMP】 2.1 简单示例

1、C中的OMP显示构造 OpenMP在C/C中通常以编译指令的方式进行使用&#xff0c;一个指令和一个结构化块组成构造。 #pragma omp parallel [clause[[,]clause]... ]#pragma omp parallel private(x) {//并行代码 } 示例代码: #include <iostream> #include <omp.h&g…

阿里与上交大提出 LLM 长文本计算新解法:可处理文本长达 1900k 字节

在实际应用大模型的过程中&#xff0c;尤其是处理长文本的上下文信息时&#xff0c;如何高效灵活地调度计算资源成为一个学术界与工业界共同关注的问题。 大语言模型所能容纳的上下文长度直接影响了诸如 ChatGPT 等高级应用与用户交互体验的优劣程度&#xff0c;这给云环境下的…

【问题】SpringBoot项目中log4j与logback的Jar包冲突问题

这篇文章主要给大家介绍了解决SpringBoot项目中log4j与logback的Jar包冲突问题,文中有详细的解决方法和冲突的原因。 SLF4J: Class path contains multiple SLF4J bindings. SLF4J: Found binding in [jar:file:/C:/Users/lx/.m2/repository/org/slf4j/slf4j-log4j12/1.7.25/sl…

Java学习笔记(六)——基本数据类型及其对应的包装类

文章目录 包装类基本数据类型及其对应的包装类获取Integer对象的方式(了解)获取Integer对象两种方式的区别(掌握) 包装类的计算&#xff1a;自动装箱和自动拆箱Integer成员方法综合练习练习1练习2练习3练习4练习5 包装类 包装类&#xff1a;基本数据类型对应的引用数据类型。 …

10本医护升职称可用的医药普刊推荐,参数大全!

医、药、护、技及医学工程等相关的人员&#xff0c;进行卫生高级职称评审时&#xff0c;需要在专业期刊上公开发表本专业学术论文&#xff0c;论文的方向、内容质量以及发表的刊物都至关重要。今天常笑医学给大家整理了10本不错的医药普刊&#xff0c;供大家参考&#xff01; 1…

x-cmd pkg | busybox - 嵌入式 Linux 的瑞士军刀

目录 简介首次用户功能特点竞品和相关作品 进一步阅读 简介 busybox 是一个开源的轻量级工具集合&#xff0c;集成了一批最常用 Unix 工具命令&#xff0c;只需要几 MB 大小就能覆盖绝大多数用户在 Linux 的使用&#xff0c;能在多款 POSIX 环境的操作系统&#xff08;如 Linu…