Android 性能优化之内存优化

news2024/9/21 2:46:24

文章目录

  • Android 性能优化之内存优化
    • 内存问题
      • 内存抖动
      • 内存泄露
      • 内存溢出
    • 检测工具
      • Memory Profiler
      • Memory Analyzer
      • LeakCanary
    • 内存管理机制
      • Java
      • Android
    • 解决
      • 内存抖动问题
        • 模拟问题代码
        • 使用Memory Profiler工具检测
        • 优化技巧
      • 内存泄露问题
        • 模拟问题代码
        • 使用LeakCanary工具检测
        • 优化技巧
      • Bitmap优化
        • Bitmap内存模型
        • 资源文件目录
        • 优化技巧
    • 源码下载

Android 性能优化之内存优化

内存问题

  • 内存抖动
  • 内存泄露
  • 内存溢出

内存抖动

内存抖动指的是在短时间内大量对象被创建和销毁,导致频繁的垃圾回收(Garbage Collection, GC)活动。这种频繁的GC活动会占用大量的CPU资源,可能导致应用程序的卡顿或性能下降。

表现:内存曲线呈锯齿状。

内存泄露

内存泄露是指应用程序持有了不再需要的对象的引用,导致这些对象无法被垃圾回收器回收,从而占用了本可以被释放的内存空间。随着时间的推移,内存泄露会导致可用内存越来越少,最终可能导致应用程序崩溃或性能下降。

内存溢出

内存溢出是指应用程序尝试分配更多的内存空间,但系统无法满足这个请求,因为已经没有足够的内存空间可供分配。这通常会导致应用程序抛出OutOfMemoryError异常。

检测工具

  • Memory Profiler
  • Memory Analyzer
  • LeakCanary

Memory Profiler

  • Memory Profiler 是 Android Studio自带的内存分析工具。
  • 实时图表展示程序内存使用情况。
  • 识别内存泄露、抖动等。
  • 提供捕获堆转储、强制GC以及跟踪内存分配的能力。

Memory Analyzer

  • 强大的 Java Heap 分析工具,查找内存泄露已经内存占用。
  • 生成整体报告、分析问题等。

LeakCanary

  • 自动内存泄露检测。
    • LeakCanary自动检测这些对象的泄露问题:Activity、Fragment、View、ViewModel、Service。
  • 官网:https://github.com/square/leakcanary

内存管理机制

Java

Java 内存结构:堆、虚拟机栈、方法区、程序计数器、本地方法栈。

Java 内存回收算法:

  • 标记-清除算法:
    1. 标记出所需要回收的对象
    2. 统一回收所有标记的对象。
  • 复制算法:
    1. 将内存划分为大小相等的两块。
    2. 一款内存用完后复制存活对象到另一块中。
  • 标记-整理算法:
    1. 标记过程与“标记-清除“算法一样。
    2. 存活对象往一端进行移动。
    3. 清除其余内存。
  • 分代收集算法:
    • 结合多种收集算法优势。
    • 新生代对象存活率低,使用复制算法。
    • 老年代对象存活率高,使用标记-整理算法。

标记-清除算法缺点:标记和清除效率不高,会产生大量不连续的内存碎片。

复制算法:实现简单,运行高效。缺点:浪费一半空间。

标记-整理算法:避免标记-清理导致的内存碎片,避免复制算法的空间浪费。

Android

Android内存弹性分配,分配值与最大值受具体设备影响。

Dalvik 回收算法和 ART 回收算法都是 Android 操作系统中用于内存管理的垃圾回收机制

Dalvik 回收算法:

  • 标记-清除算法。
  • 优点是实现简单。缺点是在标记和清除阶段都会暂停应用程序的执行,这会导致应用程序出现短暂的卡顿,影响用户体验。

Art 回收算法:

  • 压缩式垃圾回收(Compacting Garbage Collection)的算法。在标记-清除算法的基础上进行了改进,以减少垃圾回收过程中的暂停时间。
  • 并发标记:ART 引入了并发标记阶段,这意味着它可以与应用程序的执行同时进行。这减少了由于垃圾回收导致的暂停时间。
  • 清除和压缩:在清除阶段,ART 不仅清除未被标记的对象,还会压缩内存,这意味着它会将存活的对象移动到一起,减少内存碎片。这使得内存管理更高效,并减少了内存分配失败的可能性。
  • 自适应回收:ART 还引入了自适应回收的概念,这意味着它会根据应用程序的行为和内存使用模式自动调整垃圾回收的频率和方式。这使得 ART 可以更好地适应不同的应用程序需求。

LMK机制:

  • Low Memory Killer 机制。
  • 主要作用是在系统内存不足时,根据一定的优先级策略,结束一些后台进程,以释放内存,保证系统的稳定性和响应性。

解决

内存抖动问题

模拟问题代码
public class ShakeActivity extends AppCompatActivity {

    private static Handler mHandler = new Handler() {
        @Override
        public void handleMessage(@NonNull Message msg) {
            super.handleMessage(msg);
            String str = "";
            for (int i = 0; i < 10000000; i++) {
                str += i;
            }
            mHandler.sendEmptyMessageDelayed(1, 30);
        }
    };

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

    public void startClick(View view) {
        mHandler.sendEmptyMessage(1);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mHandler.removeCallbacksAndMessages(null);
    }
}
使用Memory Profiler工具检测

Memory Profiler 可以查看内存分配情况,点击“Record Java/Kotlin allocations"。

在这里插入图片描述

上面的含义:

  • Java:Java 或 Kotlin 代码分配的内存。
  • Native:C 或 C++ 代码分配的内存。
  • Graphics:图形缓冲区队列为向屏幕显示像素(包括 GL 表面、GL 纹理等等)所使用的内存。CPU共享的内存。
  • Stack:应用中的原生堆栈和 Java 堆栈使用的内存。这通常与您的应用运行多少线程有关。
  • 应用用于处理代码和资源(如 dex 字节码、经过优化或编译的 dex 代码、.so 库和字体)的内存。
  • 应用使用的系统不确定如何分类的内存。
  • 应用分配的 Java/Kotlin 对象数。此数字没有计入 C 或 C++ 中分配的对象。

下面的含义:

  • Allocations:通过 malloc()new 运算符分配的对象数量。
  • Deallocations:通过 free()delete 运算符解除分配的对象数量。
  • Allocations Size:选定时间段内所有分配的总大小,单位是字节。
  • Deallocations Size:选定时间段内释放内存的总大小,单位是字节。
  • Total Count:Allocations 减去 Deallocations 的结果。
  • Remaining Size:Allocations Size 减去 Deallocations Size 的结果。
  • Shallow Size:堆中所有实例的总大小,单位是字节。

上图分析:

在这里插入图片描述

这块地方 Allocations 和 Deallocations 的数值比较相近,同时 Shallow Size 比较大,说明可能频繁的创建和销毁对象。

在这里插入图片描述

点击后,可以查看调用栈信息,结合代码可以推测出 Handler 地方存在内存抖动问题。

优化技巧
  • 避免大量频繁创建和销毁对象。

内存泄露问题

模拟问题代码
public class CallbackManager {
    public static ArrayList<Callback> sCallbacks = new ArrayList<>();

    public static void addCallback(Callback callback) {
        sCallbacks.add(callback);
    }

    public static void removeCallback(Callback callback) {
        sCallbacks.remove(callback);
    }
}
public class LeakActivity extends AppCompatActivity implements Callback {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_leak);
        ImageView imageView = findViewById(R.id.imageView);
        Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.splash);
        imageView.setImageBitmap(bitmap);
        CallbackManager.addCallback(this);
    }

    @Override
    public void onOperate() {

    }
}
使用LeakCanary工具检测

添加依赖库:

debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.14'

发生内存泄露后,LeakCanary会生成相关信息,并自动转储:

在这里插入图片描述

上图可知:是 LeakActivity 发生内存泄露了,并显示出引用链关系。

当然也可以生成 hprof 文件,通过 Profiler 工具查看具体信息:

在这里插入图片描述

上图可知:发生了10个泄露点,其中就有 LeakActivity,点击 LeakActivity 可以查看这内存泄露的对象,并查看引用链,可知是被ArrayList持有了。

优化技巧
  • 及时回收集合元素。
  • 避免static引用过多实例。
  • 使用静态内部类。
  • 及时关闭资源对象。

Bitmap优化

使用完Bitmap后若不释放图片资源,容易造成内存泄露,从而导致内存溢出

Bitmap内存模型
  • api10之前(Android2.3.3):Bitmap对象放在堆内存,像素数据放在本地内存。
  • api10之后:均在堆内存。
  • api26之后(Android8.0):像素数据放在本地内存。使得native层的Bitmap像素数据可以和Java层的对象一起快速释放。

内存回收:

  • 在Android 3.0之前,需要手动调用Bitmap.recycle()进行Bitmap的回收。
  • 从Android 3.0开始,系统提供了更智能的内存管理,大多数情况下不需要手动回收Bitmap。

Bitmap的像素配置:

Config占用字节大小(byte)说明
ALPHA_81单透明通道
RGB_5652简易RGB色调
ARGB_8888424位真彩色
RGBA_F168Android 8.0 新增(HDR)

计算Btimap占用内存:

  • Bitmap#getByteCount()
  • getWidth() * getHeight() * 1像素占用内存
资源文件目录

资源文件问题:

  • mdpi (中等密度):大约160dpi,1x资源。
  • hdpi (高密度):大约240dpi,1.5x资源。
  • xhdpi (超高密度):大约320dpi,2x资源。
  • xxhdpi (超超高密度):大约480dpi,3x资源。
  • xxxhdpi (超超超高密度):大约640dpi,4x资源。

测试代码:

private void printBitmap(Bitmap bitmap, String drawable) {
    String builder = drawable +
            " Bitmap占用内存:" +
            bitmap.getByteCount() +
            " width:" +
            bitmap.getWidth() +
            " height:" +
            bitmap.getHeight() +
            " 1像素占用大小:" +
            getByteBy1px(bitmap.getConfig());
    Log.e("TAG", builder);
}

private int getByteBy1px(Bitmap.Config config) {
    if (Bitmap.Config.ALPHA_8.equals(config)) {
        return 1;
    } else if (Bitmap.Config.RGB_565.equals(config)) {
        return 2;
    } else if (Bitmap.Config.ARGB_8888.equals(config)) {
        return 4;
    }
    return 1;
}
// 逻辑密度
float density = metrics.density;
// 物理密度
int densityDpi = metrics.densityDpi;
Log.e("TAG", density + "-" + densityDpi);

// 1倍图
Bitmap bitmap1 = BitmapFactory.decodeResource(getResources(), R.drawable.splash1);
printBitmap(bitmap1, "drawable-mdpi");

// 2倍图
Bitmap bitmap2 = BitmapFactory.decodeResource(getResources(), R.drawable.splash2);
printBitmap(bitmap2, "drawable-xhdpi");

// 3倍图
Bitmap bitmap3 = BitmapFactory.decodeResource(getResources(), R.drawable.splash3);
printBitmap(bitmap3, "drawable-xxhdpi");

// 4倍图
Bitmap bitmap4 = BitmapFactory.decodeResource(getResources(), R.drawable.splash4);
printBitmap(bitmap4, "drawable-xxxhdpi");

// drawable
Bitmap bitmap5 = BitmapFactory.decodeResource(getResources(), R.drawable.splash);
printBitmap(bitmap5, "drawable");
/*
        3.0-480
        drawable-mdpi Bitmap占用内存:37127376 width:2574 height:3606 1像素占用大小:4
        drawable-xhdpi Bitmap占用内存:9281844 width:1287 height:1803 1像素占用大小:4
        drawable-xxhdpi Bitmap占用内存:4125264 width:858 height:1202 1像素占用大小:4
        drawable-xxxhdpi Bitmap占用内存:2323552 width:644 height:902 1像素占用大小:4
        drawable Bitmap占用内存:37127376 width:2574 height:3606 1像素占用大小:4
         */

说明:

在 mdpi 的设备上 1dp1px,在 xhdpi 的设备上 1dp2px,在 xxhdpi 的设备上 1dp==3px。

因此当前设备是 xxhdpi,因此同一张图片在 xxhdpi 资源下宽是858,在 mdpi 资源下会放大3倍宽是2574,在 xhdpi 资源下会放大1.5倍宽是1287。

优化技巧
  • 设置多套图片资源。
  • 选择合适的解码方式。
  • 设置图片缓存。

源码下载

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

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

相关文章

【常见开源库的二次开发】基于openssl的加密与解密——openssl认识与配置(一)

一、什么是openssl&#xff1f; OpenSSL 是一个开源的软件库&#xff0c;它提供了一系列加密工具和协议&#xff0c;主要用于实现安全通信&#xff0c;如在网络上的数据传输。它支持多种加密算法&#xff0c;包括对称加密、非对称加密、散列函数、伪随机数生成器、数字签名、密…

论文学习_An Empirical Study of Deep Learning Models for Vulnerability Detection

1. 引言 研究背景:近年来,深度学习漏洞检测工具取得了可喜的成果。最先进的模型报告了 0.9 的 F1 分数,并且优于静态分析器。结果令人兴奋,因为深度学习可能会给软件保障带来革命性的变化。因此,IBM、谷歌和亚马逊等行业公司非常感兴趣,并投入巨资开发此类工具和数据集。…

threadx netxduo stm32f407上实现http server

这次用的是CubeIDE CubeMX 要把NX_APP的mem分配的大一些&#xff0c;在app_azure_rtos.c中&#xff0c;我给的是40*1024&#xff0c;如果给的不够&#xff0c;会导致后面无法分配pool和thread等等 需要用到filex 要在CubeMX里面勾选上&#xff0c;还要用到http_server和dhcp …

苹果手机抹机(马来西亚)操作步骤

苹果手机抹机&#xff08;马来西亚&#xff09;操作步骤 操作环境操作步骤 操作环境 苹果6s&#xff0c;没有插卡&#xff0c;就连接上了一个wifi 操作步骤

YOLOv10改进 | Conv篇 | 利用FasterBlock二次创新C2f提出一种全新的结构(全网独家首发,参数量下降70W)

一、本文介绍 本文给大家带来的改进机制是利用FasterNet的FasterBlock改进特征提取网络&#xff0c;将其用来改进ResNet网络&#xff0c;其旨在提高计算速度而不牺牲准确性&#xff0c;特别是在视觉任务中。它通过一种称为部分卷积&#xff08;PConv&#xff09;的新技术来减少…

小白的OS Copilot 产品测评

背景 通过群友介绍才知OS Copilot 。不想错过任何优秀的AI产品。随着互联网的发展和时代的进步&#xff0c;要紧跟时代&#xff0c;了解市面上的优秀的AI科技产品。 OS Copilot 产品体验评测 1&#xff09;您的角色是什么&#xff1f;开发、运维、学生&#xff1f;如果使用O…

微软推出全新的学习网站 Microsoft Learn

微软官方宣布推出全新的学习网站 Microsoft Learn&#xff0c;供开发人员学习 Microsoft 技术。 该网站包含所有 Microsoft 产品和服务(从 HoloLens 到 Azure)的技术文档。提供了超过 80 小时的学习内容&#xff0c;涉及 Azure、Dynamics 365、PowerApps、Microsoft Flow 和 Po…

杜比全景声——空间音频技术

什么是杜比&#xff1f;是否是标清、高清、超清之上的更清晰的格式&#xff1f;杜比全景声 和传统多声道立体声的差别&#xff1f;杜比全景声音频的渲染方式&#xff1f;车载平台上杜比技术的应用&#xff1f; 杜比技术的起源 杜比实验室&#xff08;Dolby Laboratories&…

谷歌内置AI部署

感谢阅读 准备工作开启功能查看下载情况安装插件效果截图网页版地址&#xff08;需进行前面的所有步骤&#xff09; 准备工作 点我下载谷歌dev版本 注意这个版本不需要卸载之前版本 开启功能 使用下载的浏览器依次导航到下面两个地方&#xff0c;然后点击enablebypass以及en…

STM32 - PWR 笔记

PWR&#xff08;Power Control&#xff09;电源控制 PWR 负责管理 STM32 内部的电源供电部分&#xff0c;可以实现 可编程电压监测器 和 低功耗模式 的功能 可编程电压监测器&#xff08;PVD&#xff09;可以监控VDD电源电压&#xff0c;当VDD下降到PVD阀值以下或上升到PVD…

arm 、stm32、linux该如何学习?有没有先后顺序,先学什么比较好?

先讲自己&#xff0c;我是从Arduino单片机入门&#xff0c;再到stm32 &#xff0c;再开发瑞萨&#xff0c;TI&#xff0c;然后学校教了51。这是一个奇怪的学习过程&#xff0c;所以当我第一次接触51单片机的时候&#xff0c;刚好我有一些资料&#xff0c;是我根据网友给的问题精…

MacOS如何切换shell类型

切换 shell 类型 如果你想在不同的 shell 之间切换&#xff0c;以探索它们的不同之处&#xff0c;或者因为你知道自己需要其中的一个或另一个&#xff0c;可以使用如下命令&#xff1a; 切换到 bash chsh -s $(which bash)切换到 zsh chsh -s $(which zsh)$()语法的作用是运…

c++ learn fourth day

2.LOL Lovers 我们可以从 1 遍历到 len−1&#xff0c;以第 &#x1d456;i 个位置为分割线&#xff0c;统计 [0,i−1] 与 [i,len−1] 的 l 和 o 的个数&#xff0c;判断是否不相等&#xff0c;如果不相等&#xff0c;输出 i&#xff0c;然后退出即可。 如果到最后一直没有输出…

【Mac】Charles for Mac(HTTP协议抓包工具)及同类型软件介绍

软件介绍 Charles for Mac 是一款功能强大的网络调试工具&#xff0c;主要用于HTTP代理/HTTP监视器。以下是它的一些主要特点和功能&#xff1a; 1.HTTP代理&#xff1a;Charles 可以作为HTTP代理服务器&#xff0c;允许你查看客户端和服务器之间的所有HTTP和SSL/TLS通信。 …

2970.力扣每日一题7/10 Java(暴力枚举)

博客主页&#xff1a;音符犹如代码系列专栏&#xff1a;算法练习关注博主&#xff0c;后期持续更新系列文章如果有错误感谢请大家批评指出&#xff0c;及时修改感谢大家点赞&#x1f44d;收藏⭐评论✍ 目录 解题思路 解题方法 时间复杂度 空间复杂度 Code 解题思路 incre…

SDF矩形(附圆角)公式推导

SDF矩形(附圆角)公式推导 矩形 一般情况下&#xff0c;我们会使用(top_left, top_bottom), (width, height)来定义一个矩形&#xff0c;但是对于SDF而言&#xff0c;使用(centerX, centerY)&#xff0c; (HalfSizeX, HalfSizeY)会更方便一些。 假设一个矩形&#xff0c;我们先定…

利用【Python】【线性规划】优化工厂生产:实现智能资源配置与利润最大化的现代解决方案

目录 1. 问题背景和描述 1.1 问题背景 1.2 问题描述 2. 数学模型的建立 2.1决策变量 2.2 目标函数 2.3 约束条件 2.4 数学模型总结 3. 使用Python解决线性规划问题 3.1 导入必要的库 3.2 定义目标函数系数 3.3 定义不等式约束矩阵和向量 3.4 定义变量的边界 非负…

MAVLink代码生成-C#

一. 准备Windows下安装环境 Python 3.3 – 官网链接下载Python future模块 –pip3 install future TkInter (GUI 工具). – python for Windows自带&#xff0c;无需下载环境变量PYTHONPATH必须包含mavlink存储库的目录路径。 –set PYTHONPATH你的mavlink源码路径 源码下载在…

【Linux】Windows环境下配置虚拟机静态IP

当前我们虚拟机的Linux操作系统&#xff0c;其IP地址是通过DHCP服务获取的。 DHCP:动态获取IP地址&#xff0c;即每闪重启设备后都会获取一次&#xff0c;可能导致IP地址频繁变更 原因1&#xff1a;办公电脑IP地址变化无所谓&#xff0c;但是我们要远程连接到Linux系统&#x…

IDEA之Debug的使用

自定义功能图表 功能说明 光标回到Debug行 执行到光标所在行 Force Step into Trace Current Stream Chain Reset Frame 重置方法入栈