Android 性能优化之内存优化

文章目录

  • 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。

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

源码下载

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

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

相关文章

深入了解Rokid UXR2.0 SDK内置的Unity AR Glass开发组件

本文将了解到Rokid AR开发组件 一、RKCameraRig组件1.脚本属性说明2.如何使用 二、PointableUI组件1.脚本属性说明2.如何使用 三、PointableUICurve组件1.脚本属性说明2.如何使用 四、RKInput组件1.脚本属性说明2.如何使用 五、RKHand组件1.脚本属性说明2.如何使用3.如何禁用手…

数据结构与算法-动态规划-三角形最小路径和

三角形最小路径和 给定一个三角形 triangle &#xff0c;找出自顶向下的最小路径和。 每一步只能移动到下一行中相邻的结点上。相邻的结点 在这里指的是 下标 与 上一层结点下标 相同或者等于 上一层结点下标 1 的两个结点。也就是说&#xff0c;如果正位于当前行的下标 i &…

web 网络安全

Web网络安全是网络安全的一个重要分支&#xff0c;专注于保护Web应用程序、服务和网站免受各种网络威胁。学习Web网络安全涉及多个层面的知识和技能&#xff0c;以下是一些主要的学习领域&#xff1a; 一、XSS攻击 全称:&#xff1a;Cross Site Script &#xff08;跨站脚本&a…

交叉熵损失函数的使用目的(很肤浅的理解)

第一种使用方法 import torch from torch import nn # Example of target with class indices loss nn.CrossEntropyLoss() input torch.randn(3, 5, requires_gradTrue) target torch.empty(3, dtypetorch.long).random_(5) output loss(input, target) output.backward(…

今天,纷享AI正式发布,开启智能CRM新纪元

纷享销客作为国产CRM中连续四年保持近40%增长的领先品牌&#xff0c;一直在探索AICRM领域的数字化变革。 7月10日&#xff0c;纷享AI产品正式上线。与通用大模型不同&#xff0c;纷享AI是在合规之下&#xff0c;开放性的接入各种大模型平台&#xff0c;并结合纷享销客在营销服…

百度搜索框制作HTML+CSS

样品图 自制效果图&#xff08;附注释&#xff09; <!DOCTYPE html> <html lang"en"><head><!-- 定义文档的字符编码为UTF-8&#xff0c;以支持中文等多语言字符 --><meta charset"UTF-8" /><!-- 设置页面在不同设备上的…

人形机器人头部结构设计

我又回来啦&#xff01;电机部分的教程会继续更新咯~ 前几天做了成图增材赛道&#xff0c;也算4个月以来本人做过最复杂的结构项目。 不知结果会怎么样&#xff0c;但我也尽全力啦&#xff01; 把说明书发在这里&#xff0c;STL已发GitHub&#xff0c;链接&#xff1a; zysampo…

探索东芝 TCD1304DG 线性图像传感器的功能

主要特性 高灵敏度和低暗电流 TCD1304DG 具有高灵敏度和低暗电流&#xff0c;非常适合需要精确和可靠图像捕捉的应用。传感器包含 3648 个光敏元件&#xff0c;每个元件尺寸为 8 m x 200 m&#xff0c;确保了出色的光灵敏度和分辨率。 电子快门功能 内置的电子快门功能是 T…

Java 期末速成

其他题 import java.util.*; public class Test {public static void main(String[] args) {Scanner scanner new Scanner(System.in);int arr[] new int[100];int value scanner.nextInt();int s scanner.nextLine(); // 键盘输入多个字符int result 0;System.out.print…

C++ 调用Halcon引擎,脚本调试代码

一&#xff0c;背景&#xff1a;C调用halcon最常见的方式便是转C代码&#xff0c;然后封装成函数或者类库。另外一种方式是调用Halcon脚本&#xff0c;不需要转换成C代码&#xff0c;Debug的时候&#xff0c;可以直接跳入halcon脚本&#xff0c;单步查看每一行算法执行情况&…

仓库的数据管理如何做?

在当今这个数字化飞速发展的时代&#xff0c;仓库作为供应链的核心环节&#xff0c;其数据管理的重要性日益凸显。一个高效、精准的仓库数据管理体系&#xff0c;不仅能够显著提升物流效率&#xff0c;降低运营成本&#xff0c;还能增强企业的市场竞争力。那么&#xff0c;仓库…

使用八股搭建神经网络

神经网络搭建八股 使用tf.keras 六步法搭建模型 1.import 2.train, test 指定输入特征/标签 3.model tf.keras.model.Sequential 在Squential,搭建神经网络 4.model.compile 配置训练方法&#xff0c;选择哪种优化器、损失函数、评测指标 5.model.fit 执行训练过程&a…

高压线束屏蔽效能测试之管中管法、线注入法

一、引言 上期推文介绍了高压线束屏蔽效能测试方法三同轴法&#xff0c;本篇文章将继续介绍高压线束相关测试方法——管中管法和线注入法。 二、管中管法 1、一般要求 管中管法参照IEC62153-4-7标准对高低压连接器进行零部件级屏蔽效能测试。在测试时&#xff0c;通过金属延长管…

安卓腾讯桌球多功能助手直装版

安卓13自测效果&#xff0c;安卓12-安卓12以下一定可以的&#xff0c;QQ登陆的话扫码登陆&#xff0c;两个手机&#xff0c;一个扫码&#xff0c;一个游戏&#xff0c;一个手机的话&#xff0c;你可以下载个虚拟机&#xff0c;然后本机直装&#xff0c;用虚拟机QQ扫码即可 微信…

使用资源编排 ROS 轻松部署单点网站——以 WordPress 为例

介绍 WordPress是一款免费开源的网站内容管理系统&#xff08;CMS&#xff09;&#xff0c;它可以帮助用户简单快捷地创建和管理自己的网站&#xff0c;包括博客、新闻网站、电子商务网站、社交网络等等。WordPress 有丰富的主题和插件库&#xff0c;使得用户可以轻松地为网站…

点线面推进未来智造

如今&#xff0c;宁波拥有门类齐全的制造业体系&#xff0c;形成了以石油化工、汽车及零部件、电工电器、纺织服装等为支柱的产业集群。 宁波工业的发展并非一蹴而就&#xff0c;蓝卓总经理谭彰详细解读了宁波制造业的发展历程与当下目标&#xff0c;从工业小市到工业大市、工业…

【深度学习】第5章——卷积神经网络(CNN)

一、卷积神经网络 1.定义 卷积神经网络&#xff08;Convolutional Neural Network, CNN&#xff09;是一种专门用于处理具有网格状拓扑结构数据的深度学习模型&#xff0c;特别适用于图像和视频处理。CNN 通过局部连接和权重共享机制&#xff0c;有效地减少了参数数量&#x…

阿一课代表今日分享之使用dnscat2 进行dns隧道反弹shell(直连模式linux对linux)

DNS介绍 DNS是域名系统(Domain Name System)的缩写&#xff0c;是因特网的一项核心服务&#xff0c;它作为可以将域名和IP地址相互映射的一个分布式数据库&#xff0c;能够使人更方便的访问互联网&#xff0c;而不用去记住能够被机器直接读取的IP数串。 DNS的记录类型有很多&a…

数据结构--二叉树收尾

1.二叉树销毁 运用递归方法 分类&#xff1a; 根节点左子树右子树&#xff08;一般都是这个思路&#xff0c;不断进行递归即可&#xff09; 选择方法&#xff08;分析)&#xff1a; 前序&#xff1a;如果直接销毁根就无法找到左子树右子树 中序&#xff1a;也会导致丢失其…

非关系型数据库(NoSQL)与 关系型数据库(RDBMS)的比较

非关系型数据库&#xff08;NoSQL&#xff09;与 关系型数据库&#xff08;RDBMS&#xff09;的比较 一、引言二、非关系型数据库&#xff08;NoSQL&#xff09;2.1 优势 三、关系型数据库&#xff08;RDBMS&#xff09;3.1 优势 四、结论 &#x1f496;The Begin&#x1f496;…