Android 性能优化之内存优化——重识内存

news2024/11/17 1:38:00

我们知道,手机的内存是有限的,如果应用内存占用过大,轻则引起卡顿,重则导致应用崩溃或被系统强制杀掉,更严重的情况下会影响应用的留存率。因此,内存优化是性能优化中非常重要的一部分。但是,很多开发者对内存的认识还停留在应用开发这一层,平时只是参考网上的方案,对内存进行比较浅显的优化。想要深入进行内存优化,我们需要从操作系统的层面了解内存是怎么管理的,又是如何被使用的

可能会有人疑问:“为什么做个内存优化需要从操作系统层了解内存呢?”我们确实可以在网上搜到很多内存优化的文章,但它们都是从上层应用出发进行优化的,而不同的应用因为环境不一样、业务不一样,很多优化方法都不能通用。因此,只有当我们从底层掌握了内存的原理,从下而上地制定优化方案,才能适用于任何业务,甚至当我们转型到 iOS、前端或者后端都能通用。

接下来,我们就从操作系统底层出发,重新认识内存。

我们先将目光放到操作系统的早期,在这个环境下,程序都是直接操作物理内存的。比如一个程序执行如下指令:

MOV REGISTER1,0

计算机会将位置为 0 的物理内存中的内容移到 REGISTER1 的寄存器中。在这种情况下,如果第二个程序在 0 的位置写入一个新的值,就会擦掉第一个程序存放在相同位置上的所有内容,导致第一个程序崩溃。

正因为应用程序可以直接操作物理内存,所以我们完全可以修改其他程序在内存中的数据,导致程序崩溃或者产生安全问题。因此,对当时的操作系统来说,同时运行多个程序很困难。

为了解决这个问题,我们自然而然会想到:不允许应用程序直接操作物理内存。于是虚拟内存的技术诞生了。

为了更好地了解什么是虚拟内存,我们先看看早期直接操作物理内存系统下的内存模型长什么样。从下面内存模型的简化图中我们可以看到,物理内存中存在两块数据,一个是操作系统的数据,一个是应用程序的数据。除此之外,其实还会有设备驱动程序的数据,它们不是我们了解的重点就先不列上去了。

什么是虚拟内存?

虚拟内存技术相当于给每个程序一个独占且连续的内存,比如 32 位系统下是 4G(2^32),只不过这个内存是虚拟的。同时,虚拟内存需要能够映射到真实的物理内存。简化的内存模型如下

从上图的简化内存模型,我们可以看到,每个程序都可以独享一块虚拟内存。在 Linux 系统上,一个进程代表着一个程序,这里我们可以理解成每个进程都独享一块虚拟内存,这块虚拟内存在 32 位系统下是 4G(2^32),64 位系统下是 2^48,即 256TB(这里不是 2^64,是因为 256TB 已经足够大了,如果用 2^64,会有大量的寻址空间浪费)。

其次,虚拟内存都由应用程序和操作系统这两部分组成。其中,应用程序这部分虚拟内存是应用独占的,操作系统这部分虚拟内存则由所有进程共享,而所有进程的操作系统这部分虚拟内存都指向了同一段物理内存。虚拟内存到物理内存的映射由操作系统来实现,操作系统在做映射操作时会寻找可用的物理内存,不可能出现覆盖其他数据的情况,这让同时运行多个程序成为了可能。

上图的虚拟内存是一个简化的模型。实际上,虚拟内存和物理内存都是按照页来管理和映射的,一页的大小为 4KB,我们来看一个 32 位 Android 系统、物理内存为 2G 的设备的内存模型。

可以看到,物理内存和虚拟内存是通过 4K 大小的页一一对应的。这个时候,如果我们再用文章最开头的那个指令:

MOV REGISTER1,0

在虚拟内存技术的加持下,此时计算机就不会直接将物理地址为 0 的内存移到 REGISTER1 寄存器了,而是先寻找虚拟地址 0 对应的物理地址 4096,然后将物理地址为 4096 的内容移到 REGISTER1 寄存器中

MOV REGISTER1,4096

虚拟地址转换成物理地址是由计算机的内存管理单元(MMU)完成的,它属于硬件部分而不是系统软件部分,所以转换速度很快。

虚拟内存的内存模型

知道了虚拟内存由操作系统和应用程序两部分组成,并且虚拟内存都由页来维护和管理之后,我们再深入了解一下 Linux 系统中虚拟内存的内存模型,它需要和 Linux 系统的可执行文件,也就是 ELF 文件一起配合来看。

为了让你理解起来更简单,这里的 ELF 文件格式也被我简化了,后面用到的时候再进行深入介绍。在 Linux 系统中,存放操作系统的虚拟内存区域被称为内核空间,剩下的存放应用的虚拟内存区域称为用户空间。 内核空间占用了 1G,位于虚拟地址的高地址区域,而 ELF 文件的一些数据,是存放在低地址的区域(即从地址 0 开始)。下面我详细解释一下内存模型中的几个区域

  1. 栈:由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。
  2. 堆:动态内存分配,可以由开发者自己分配和释放(malloc 和 free 函数实现),Android 开发时不需要我们手动分配和释放,因为虚拟机程序已经帮我们做了。堆的开始地址由变量 start_brk 描述,堆的当前地址由变量 brk 描述。
  3. BSS:存放全局未初始化,静态未初始化数据。
  4. 数据段:存放全局初始化,静态初始化数据。
  5. 程序代码区:存放的是 ELF 文件代码段。

可以看到,栈内存的分配是从上到下的,而堆内存的分配是从下到上的,这种方式可以最大程度利用虚拟内存的空间。

虚拟内存分配

前面我们已经知道,应用是没法直接操作物理内存的,所以我们在开发 App 时分配的内存实际都是虚拟内存。那么我们怎么申请虚拟内存呢?

开发 Android 应用时,并不需要我们自己去分配内存,直接 new 一个对象,声明一个变量或者常量即可,也不需要我们自己去做释放,但所有的数据都需要内存,这些都是虚拟机帮我们做。虚拟机分配申请内存主要使用的是 malloc() 函数,它是 C 语言库的一个标准函数。

void *malloc(size_t size)

malloc 函数是一个 C 语言库的函数,所以它分配内存最终还是得调用 Linux 系统提供的函数,让 Linux 内核去帮我们申请一块内存。内核会调用 mmap() 函数,在堆中分配我们想要的内存空间大小。 mmap() 函数是 Linux 系统一个很重要的函数,我们需要深刻认识它。

void *mmap(void *addr,size_t length,int prot,int flags,int fd, off_t offset);
  • 参数 addr 指向欲映射的内存起始地址,通常设为 NULL,代表让系统自动选定地址,映射成功后返回该地址;

  • 参数 length 表示将文件中多大的部分映射到内存;

  • 参数 prot 指定映射区域的读写权限;

  • 参数 flags 指定映射时的特性,如是否允许其他进程映射这段内存;

  • 参数 fd 指定映射内存的文件描述符;

  • 参数 offset 指定映射位置的偏移量,一般为 0。

mmap 函数有 2 种用法:

  1. 映射磁盘文件到用户空间中;
  2. 匿名映射,不映射磁盘文件,而是向映射区申请一块内存,此时的 fd 入参传 -1。

第 1 种用法可以让我们读文件的效率更高(比如 Android 读取 dex 文件就是通过 mmap 来提高读取速度),也可以用来实现数据跨进程传输(比如 Android 共享内存机制、Binder 通信都是通过 mmap 来实现的)。malloc() 函数使用了 mmap 函数的第 2 种用法,即在 Heap 区域中申请一块内存。

需要注意的,这里申请的内存都是虚拟内存,并且这个时候并不会分配真正的物理内存,只有当我们真正要往这块虚拟内存区域写入数据时,操作系统检查到对应的虚拟内存没有映射到物理内存,便会发生缺页中断,然后分配一块同样大小的物理内存,并建立映射关系。这是一种懒加载技术,也是内存优化的方案之一。

malloc() 函数在申请内存小于 128k 时会使用 sbrk() 函数,sbrk() 会将堆顶指针(即前面提到的 brk)向高地址移动,获得新的虚存空间,这些策略都是基于性能考虑的。比如 Android 虚拟机在分配大对象时,也会专门放在 LargeObjectSpcace 中,这些就不展开讲了。至于 Linux 系统是如何发生缺页中断,如何分配物理内存,如何建立映射关系的,都属于 Linux 系统相关知识了,更详细的知识点会在后面的篇章中结合实战项目穿插着讲解。

小结

事实上,直接操作物理内存的操作系统并没有消失,我们现在的嵌入式设备,如冰箱,微波炉等等都能直接操作物理内存。这其实也符合它们的使用场景,直接操作物理内存会让性能开销更小,操作也更方便。但需要同时运行多个软件的系统都有虚拟内存,可以说虚拟内存是现代操作系统最重要的发明之一了。

当我们重新认识了内存后,我们再来看内存优化,它其实分为两部分。

  • 一是物理内存的优化:也就是这个程序实际消耗的物理内存。
  • 二是虚拟内存的优化:在前面我们也知道了 32 位机只有 3G 的虚拟内存可用,所以一个比较大的Android 程序,很容易就会出现虚拟内存不足的情况(64 位系统就完全不用担心这个问题)。

为了帮助能更好的掌握性能优化相关知识点,这准备了 性能优化知识点汇总和Android 性能监控框架 的学习文档,中间记录了 启动优化、内存优化、UI优化……等知识点,大家可参考:https://0a.fit/dNHYY


为了帮助能更好的掌握性能优化相关知识点,这准备了 性能优化知识点汇总和Android 性能监控框架 的学习文档,中间记录了 启动优化、内存优化、UI优化……等知识点,大家可参考:https://0a.fit/dNHYY

内存优化

UI优化

网络优化

Bitmap优化与图片压缩优化

多线程并发优化与数据传输效率优化,大家可参考:https://0a.fit/dNHYY

启动优化

体积包优化

《Android 性能监控框架》

大家可参考:https://0a.fit/dNHYY

《Android Framework学习手册》

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

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

相关文章

66-86-javajvm-堆

66-javajvm-堆: 堆的核心概述 堆与进程、线程 一个进程对应一个JVM实例一个JVM实例对应一个堆空间进程包含多个线程,所以线程之间共享同一个堆空间 对堆的认识 一个JVM实例只存在一个堆内存,堆也是Java内存管理的核心区域。Java堆区在JVM启动…

HashMap原理

在Java编程语言中,最基本的结构就是两种,一种是数组,一种是模拟指针(引用),所有的数据结构都可以用这两个基本结构构造,HashMap也一样。当程序试图将多个 key-value 放入 HashMap 中时,以如下代码片段为例:…

P1182 数列分段 Section II——二分答案

数列分段 Section II 题目描述 对于给定的一个长度为N的正整数数列 A1∼NA_{1\sim N}A1∼N​,现要将其分成 MMM(M≤NM\leq NM≤N)段,并要求每段连续,且每段和的最大值最小。 关于最大值最小: 例如一数列…

NCTF web总结与复现

前言 打完NCTF休息了一下,总体感觉还行,学到了很多。 calc 这一题也卡了我很久,因为复现过DASCTF三月赛,一直在想着有没有可以替代反引号或绕过的方法,搞了好久都没出,在学长的提示下学到了一个方法&…

最新出炉的阿里巴巴面试题及答案汇总(513页)

前言 秋招已经结束了,不知道各位有没有拿到自己心仪的offer?最近有不少粉丝去阿里巴巴面试了,回来之后我整理成了一份手册java面试时常用到的面试题(附答案)那么今天分享给大家,祝愿大家都能找到满意的工作…

HTML期末作业课程设计期末大作业——我的美丽家乡湛江 海鲜之都HTML+CSS+JavaScript

家乡旅游景点网页作业制作 网页代码运用了DIV盒子的使用方法,如盒子的嵌套、浮动、margin、border、background等属性的使用,外部大盒子设定居中,内部左中右布局,下方横向浮动排列,大学学习的前端知识点和布局方式都有…

python爬虫实战之逆向分析酷狗音乐

文章目录前言一、请求分析二、逆向思路三、全部代码总结前言 声明:本文章只是用于学习逆向知识,仅供学习,未经作者同意禁止转载 对于爬虫而言,不管是什么类型的都会遵循这几个步骤 获取目标url分析请求数据逆向解密数据伪造请求清…

算法日常训练12.5

首先有个很大的进步,看见困难题我没选择做逃兵跑路,这点起码是进步了,虽然算法能力还是那么拉,但是起码敢不自量力地分析一下。。。还能看题解理解下。 先找题解中最简单地一种超时方法开始理解,使用动态规划&#xff…

线程基础概念

1.线程基础 现代软件系统中,除了进程之外,线程也是一个十分重要的概念。特别是随着CPU频率增长开始出现停滞,而开始向多核方向发展。多线程,作为实现软件并发执行的一个重要的方法,也开始具有越来越重要的地位。 什么…

[本人毕业设计] 别踩白块_计算机科学与技术_前端H5游戏毕设

摘 要 本文详细介绍了网页版躲避白色钢琴块音乐游戏的设计和实现。由于游戏软件安装占据较大的空间与安装时间,而且步骤繁琐,用常规的游戏安装方法不能取得便捷的游戏安装体验。网页游戏是一种基于在网络游戏中被广泛应用,网页游戏更具有便捷…

【Tensorflow深度学习】实现手写字体识别、预测实战(附源码和数据集 超详细)

需要源码和数据集请点赞关注收藏后评论区留言私信~~~ 一、数据集简介 下面用到的数据集基于IAM数据集的英文手写字体自动识别应用,IAM数据库主要包含手写的英文文本,可用于训练和测试手写文本识别以及执行作者的识别和验证,该数据库在ICDAR1…

对副业的选择无论是自媒体还是 Python接单 ,始终绕不开IT行业。

前言 这个年代,成年人的日子活成了一部苦情戏。十年前,5000块钱工资还能过的自由自在;今天,估计连车贷,房贷,信用卡都不够还。所以一些想要改变现状的朋友,选择了副业这种形式,副业…

【Linux】Shell脚本详解

目录一.概述二.Linux提供的Shell解析器三.Shell入门1.执行一个简单的shell脚本2.脚本常用的执行方法四.变量1.系统预定义变量2.自定义变量3.特殊变量五.运算符六.条件判断1.单条件判断2.多条件判断七.流程控制(重点)1.if判断2.case语句3.for循环4.while循环八.read读取控制台输…

【论文简述】 Point-MVSNet:Point-Based Multi-View Stereo Network(ICCV 2019)

一、论文简述 1. 第一作者:Rui Chen、Songfang Han 2. 发表年份:2019 3. 发表期刊:ICCV 4. 关键词:MVS、深度学习、点云、迭代改进 5. 探索动机:很多传统方法通过多视图光度一致性和正则化优化迭代更新&#xff…

C语言实例|使用C程序优雅地杀掉其它程序进程

C语言文章更新目录 C语言学习资源汇总,史上最全面总结,没有之一 C/C学习资源(百度云盘链接) 计算机二级资料(过级专用) C语言学习路线(从入门到实战) 编写C语言程序的7个步骤和编程…

FPGA 20个例程篇:18.SD卡存放音频WAV播放(中)

第七章 实战项目提升,完善简历 18.SD卡存放音频WAV播放(中) 如图1所示是WM8731中11个寄存器功能说明概况图,我们需要对照手册,再去深入了解WM8731中的11个寄存器,怎么去配置这些寄存器达到预期的效果&…

了解3dmax坐标系

3dmax具有多种坐标系,其类别如下;默认的是View坐标系; 新建一个茶壶,此时默认是View坐标系; 切换到屏幕坐标系,看一下如下图;要保持视口区域激活; 根据资料,屏幕坐标系&a…

园区如何快速实现数据可视化分析?

对于园区运营方来说,如果没有专业针对性的管理方案以及管理系统辅助的话,实现园区可视化管理的难度非常大,而且操作成本会很高。但如果园区运营方选择引进快鲸智慧楼宇推出的园区数据孪生可视化管理系统的话就会简单很多。 快鲸智慧楼宇数据孪…

视频学习|Springboot在线学习系统

作者主页:编程千纸鹤 作者简介:Java、前端、Pythone开发多年,做过高程,项目经理,架构师 主要内容:Java项目开发、毕业设计开发、面试技术整理、最新技术分享 收藏点赞不迷路 关注作者有好处 文末获得源码 …

对文本进行情感分析(分类)snownlp模块

【小白从小学Python、C、Java】 【计算机等级考试500强双证书】 【Python-数据分析】 对文本进行情感分析(分类) snownlp模块 选择题 对于以下python代码表述错误的一项是? from snownlp import SnowNLP myText我爱学python! print("【显示】text"…