广告
返回顶部
首页 > 资讯 > 移动开发 >详解android是如何管理内存的
  • 530
分享到

详解android是如何管理内存的

2024-04-02 19:04:59 530人浏览 安东尼
摘要

目录前言Java Heap进程内存分配内存不足管理GC 垃圾回收内核交换守护进程低内存终止守护进程最后前言 很高兴遇见你~ 内存优化一直是 Android 开发中的一个非常重要的话

前言

很高兴遇见你~

内存优化一直是 Android 开发中的一个非常重要的话题,他直接影响着我们 app 的性能表现。但这个话题涉及到的内容很广且都偏向底层,让很多开发者望而却步。同时,内存优化更加偏向于“经验知识”,需要在实际项目中去应用来学习

因而本文并不想深入到底层去讲内存优化的原理,而是着眼于宏观,聊聊 android 是如何分配和管理内存、在内存不足的时候系统会如何处理以及会对用户造成什么样的影响。

Android 应用基于 JVM 语言进行开发,虽然 Google 根据移动设备特点开发了自家的虚拟机如 Dalvik、ART,但依旧是基于 JVM 模型,在堆区分配对象内存。因此 Java heap(java 堆)是android应用内存分配和回收的重点。其次,移动设备的 RAM 非常有限,如何为进程分配以及管理内存也是重中之重。

文章的主要内容是分析 Java heap、RAM 的内存管理,以及当内存不够时 android 会如何处理。

那么,我们开始吧。

Java Heap

Java Heap,也就是 JVM 中的堆区。简单回顾一下 JVM 中运行时数据区域的划分:

  • 橙色区域的方法栈以及程序计数器属于线程私有,主要存储方法中的局部数据。
  • 方法区主要存储常量以及类信息,线程共享。
  • 堆区主要负责存储创建的对象,几乎一切对象的内存都在堆区中分配,同时也是线程共享。

我们在 android 程序中使用如 Object o = new Object() 代码创建的对象都会在堆区中分配一块内存进行存储,具体如何分配由虚拟机解决而不需要我们开发者干预。当一个对象不再使用时, JVM 中具有垃圾回收机制(GC),会自动释放堆区中无用的对象,重新利用内存。当我们请求分配的内存已经超过堆区的内存大小,则会抛出 OOM 异常。

在 android 中,堆区是一个由 JVM 逻辑划分的区域,他并不是真正的物理区域。堆区并不会直接全部映射和他等量大小的物理内存,而是到了需要使用时,才会去建立逻辑地址和物理地址的映射:

这样可以给应用分配足够的逻辑内存大小,同时也不必在启动时一次性分配一大块的物理内存。在相同大小的内存中,可以运行更多的程序。

当堆区进程 GC 之后,释放出来多余的空闲内存,会返还给系统,减少物理内存的占用。但这个过程涉及到比较复杂的系统调用,若释放的内存较为少量,可能得不偿失,则无需返还给系统,在堆区中继续使用即可。

在 GC 过程中,如果一个对象不再使用,但是其所占用的内存无法被释放,导致资源浪费,这种现象称为内存泄漏。内存泄露会导致堆区中的对象越来越多,内存的压力越来越大,甚至出现 OOM 。因此,内存泄露是我们必须要尽量避免的现象。

进程内存分配

堆区的内存分配,属于进程内的内存分配,由进程自己管理。下面讲一个应用,系统是如何为其分配内存的。

系统的运行内存,即为我们常说的 RAM ,是应用的运行空间。每个应用必须装入内存中才可以被执行:

  • 我们安装的应用进程都位于硬盘中
  • 当一个应用被执行时,需要装入到 RAM 中才能被执行(zRAM 是为了压缩数据节省空间而设计,后续会讲到)
  • CPU 与 RAM 交互,读取指令、数据、写入数据等

RAM 的大小为设备的硬件内存大小,是非常宝贵的资源。现代手机常见的运存是6G、8G或者12G,一些专为游戏研发的手机甚至有18G,但同时价格也会跟上去。

Android 采用分页存储的方式把一个进程存储到 RAM 中。分页存储,简单来说就是把内存分割成很多个小块,每个应用占用不同的小块,这些小块也可以称为页:

前面讲到,进程的堆区并不是一次性分配,当需要分配内存时,系统会为其分配空闲的页;当这些页被回收,那么有可能被返还到系统中。

这里的页、块概念涉及到操作系统的分页存储,这里并不打算展开详细讲解,有兴趣的读者可以自行了解:分页存储-维基百科。本文中的“页”与“块”可以不严谨地理解为同个概念,为了帮助理解这里不进行详细地区分。

分配给进程的页可以分为两种类型:干净页、脏页:

  • 干净页:进程从硬盘中读取数据或申请内存之后未进行修改。这种类型的页面在内存不足的时候可以被回收,因为页中存储的数据可通过其他的途径复原。
  • 脏页:进程对页中的数据进行了修改或数据存储。这类页面不能被直接回收,否则会造成数据丢失,必须先进行数据存储。

zRAM,是作为 RAM 中的一个分区,当内存不足时,可以把一些类型的页压缩之后存储在zRAM中,当需要使用的时候再从zRAM中调出。通过压缩来节省应用的空间占用,同时不需要与硬盘进行调度,提高了速度。

这里需要理解的一个点是:内存中的操作速度要远远比硬盘操作快。即使与zRAM的调入和调出需要压缩和解压,其速度也是比与硬盘交互快得多。

内存不足管理

前面我们一直强调,移动设备的内存容量是非常有限的,需要我们非常谨慎地去使用它。幸运的是,JVM 和 android 系统早就帮我们想到了这一点。

面对不同的内存压力,android 会有不同的应对策略。从低到高依次是 GC、内核交换守护进程释放内存、低内存终止守护进程杀死进程释放内存;他们的代价也是逐步上升。下面我们依个来介绍一下。

GC 垃圾回收

GC 属于 JVM 内部的内存管理机制,他管理的内存区域是堆区。当我们创建的对象越来多,堆区的压力越来越大时,GC 机制就会启动,开始回收堆区中的垃圾对象。

辨别一个对象是否是垃圾,虚拟机采用的是可达性分析法。即从一些确定活跃有用的对象出发,向下分析他的引用链;如果一个对象直接或者间接这些对象所引用,那么他就不是垃圾,否则就是垃圾。这些确定活跃有用的对象称为 GC Roots:

如上图,其中绿色的对象被 GC Roots 直接或间接引用,则不会被回收;灰色的对象没有被引用则被标记为垃圾
GC Roots对象的类型比较常见的是静态变量以及栈中的引用。静态变量比较好理解,他在整个进程的执行期间不会被回收,因此他肯定是有用的。栈,这里指的是 JVM 运行数据区域中的方法栈,也就是局部变量引用,在方法执行期间肯定是活跃的。由于方法栈属于线程私有,因此这里等于活跃线程持有的对象不会被回收。

因此,如果一个对象对于我们的程序不再使用,则必须解除 GC Roots 对其的引用,否则会造成内存泄露。例如,不要把 activity 赋值给一个静态变量,这样会导致界面退出时activity无法被回收。

GC 也并不是直接对整个堆区进行回收,而是将堆区中的对象分成两个部分:新生代、老年代。

刚创建的对象大都会被回收,而在多次回收中存活的对象则后续也很少被回收。新生代中存储的对象主要是刚被创建不久的对象,而老年代则存储着那些在多次 GC 中存活的对象。那么我们可以针对这些不同特性的对象,执行不同的回收算法来提高GC性能:

  • 对于新创建的对象,我们需要更加频繁地对他们进行GC来释放内存,且每次只需要记录需要留下来的对象即可,而不必要去标记其他大量需要被回收的对象,提高性能。
  • 对于熬过很多次GC的对象,则可以以更低的频率对他门进行GC,且每次只需要关注少量需要被回收的对象即可。

具体的垃圾回收算法就不继续展开了,了解到这里就可以。感兴趣的读者可以阅读相关书籍。

单次的垃圾回收速度是很快的,甚至我们都无法感知到。但当内存压力越来越大,垃圾回收的速度跟不上内存分配的速度,此时就会出现内存分配等待 GC 的情况,也就是发生了卡顿。同时,我们无法控制 GC 的时机,JVM 有一套完整的算法来决定什么时候进行 GC。假如在我们滑动界面的时候触发 GC ,那么展示出来的就是出现了掉帧情况。因此,做好内存优化,对于 app 的性能表现非常重要。

内核交换守护进程

GC 是针对于 Java 程序内部进行的优化。对于移动设备来说,RAM 非常宝贵,如何在有限的 RAM 资源上进行分配内存,也是一个非常重要的话题。

我们的应用程序都运行在 RAM 中,当进程不断申请内存分配,RAM 的剩余内存达到一定的阈值时,会启动内核交换守护进程来释放内存以满足资源的分配。

内核交换守护进程,是运行在系统内核的一个进程,他主要的工作时回收干净页、压缩页等操作来释放内存。前面讲到,android 是基于分页存储的操作系统,每个进程都会被存储到一些页中。分页的类型有两种:干净页、脏页:

  • 当内核交换守护进程启动时,他会把干净页回收以释放内存。当进程再次访问干净页时,则需要去硬盘中再次读取。
  • 对于脏页,内核交换守护进程会把他们压缩后放入 zRAM 中。当进程访问脏页时,则需要从zRAM中解压出来。

通过不断回收和压缩分页的方式来释放内存,以满足新的内存请求。使用此方式释放的内存也无法满足新的内存请求时,android 会启动低内存终止守护进程,来终止一些低优先级的进程。

低内存终止守护进程

当 RAM 的被占用内存达到一定的阈值,android 会根据进程的优先级,终止部分进程来释放内存。当低内存终止守护进程启动时,说明系统的内存压力已经非常大了,这在一些性能较差的设备中经常出现。

进程的优先级从高到低排序如下,优先级更高的进程会优先被终止:

图片来源:developer.android.google.cn/topic/perfo…

从上到下依次是:

  • 后台应用:使用过的 app 会被缓存在后台,下一次打开可以更加快速地进行切换。当内存不足时,此类应用会最快被杀死。
  • 上一个应用:例如从微信跳转到浏览器,此时微信就是上一个应用。
  • 主屏幕应用:这是启动器应用,也就是我们的桌面。如果这个进程被kill了,那么返回桌面时会暂时黑屏。
  • 服务:同步服务、上传服务等等
  • 可觉察的应用:例如正在播放的音乐软件,他可以被我们感知到,但是不在前台。
  • 前台应用:当前正在使用的应用,如果这个应用被kill了,需要向用户报崩溃异常,此时的体验是极差的。
  • 持久性(服务):这些是设备的核心服务,例如电话和 WLAN。
  • 系统:系统进程。这些进程被终止后,手机可能即将重新启动,就像手机突然卡死重启。
  • 原生:系统使用的极低级别的进程,例如我们的内核交换守护进程。

当内存不足,会按照上面的规则,从上到下来终止进程,获得内存资源。这也就是为什么在 android 中我们的后台应用一直被杀死。为了避免我们的应用被优化,内存优化就显得非常重要了。

最后再来回顾一下:

图片来源:www.youtube.com/watch?v=w7K…

  • 在0-1阶段,系统的内存资源足够,程序请求内存分配,系统会不断地使用空闲页来满足应用的内存请求
  • 在1-2阶段,系统的可利用内存下降到一个阈值,程序继续请求内存分配,内核交换守护进程启动,开始释放缓存来满足内存请求
  • 在2-3阶段,系统的被利用内存达到一个阈值,系统将启动低内存终止守护进程来杀死进程释放内存

最后

我们文章分析了 android 是如何对内存进行分配以及低内存时如何释放内存来满足内存请求。可以很明显看到,当内存不足时,会严重影响我们 app 的体验甚至整个用户手机的体验:

  • 当内存不足会造成频繁GC、回收干净页、回写缓存,导致应用缓慢、卡顿
  • 如果设备内存一直不够,那么会一直杀死进程影响用户体验,特别是这些进程是用户非常在意的如游戏、微信
  • 内存占用过高会让app在后台被杀死、或者让用户的其他app被杀死、甚至整个系统无法运行而直接崩溃重启,
  • 不是所有的设备都有着高内存,有着设备只有很少的内存,在一些性能较差的设备上甚至会无法运行,这样我们就失去了这些设备的市场

反观现在国内的很多 app,有如扣扣、t宝、iqy,在我这个三年前的机器上运行会发生严重卡顿,偶尔还有ANR崩溃的出现;而当我去测试了youto、tele、Twit等 app ,发现基本不会发生卡顿,甚至在 youto 这样有大量图片视频加载的 app 界面切换也尽享丝滑。这两种 app 的体验是有着天壤之别的。

本文没有讲如何进行内存优化,是因为这一块的内容设计到的太广太深,无法在这篇文章中一并介绍。文章的目的只是为了帮助读者了解android是如何管理内存以及内存不足可能造成的后果,对内存的重要性能有一个感性的认知。

以上就是详解android是如何管理内存的的详细内容,更多关于Android 管理内存的资料请关注编程网其它相关文章!

以上就是详解android是如何管理内存的的详细内容,更多关于Android 管理内存的资料请关注编程网其它相关文章!

--结束END--

本文标题: 详解android是如何管理内存的

本文链接: https://www.lsjlt.com/news/122627.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • 详解android是如何管理内存的
    目录前言Java Heap进程内存分配内存不足管理GC 垃圾回收内核交换守护进程低内存终止守护进程最后前言 很高兴遇见你~ 内存优化一直是 Android 开发中的一个非常重要的话...
    99+
    2022-11-12
  • 详解Swift的内存管理
    目录内存管理weak循环引用闭包的循环引用self的循环引用内存访问冲突指针指针分类获得变量的指针创建指针指针之间的转换内存管理 和OC一样, 在Swift中也是采用基于引用计数的A...
    99+
    2022-11-12
  • JVM内存管理之JAVA语言的内存管理详解
    引言内存管理一直是JAVA语言自豪与骄傲的资本,它让JAVA程序员基本上可以彻底忽略与内存管理相关的细节,只专注于业务逻辑。不过世界上不存在十全十美的好事,在带来了便利的同时,也因此引入了很多令人抓狂的内存溢出和泄露的问题。可怕的事情还不只...
    99+
    2023-05-31
    jvm 内存管理 java
  • 详解C/C++内存管理
    目录C/C++内存分布C语言中动态内存管理方式C++中动态内存管理方式new和delete操作内置类型new和delete操作自定义类型operator new和operator d...
    99+
    2022-11-12
  • SQLServer的内存管理架构详解
    目录一、Windows的虚拟内存管理器二、SQL Server 内存体系结构2.1、传统(虚拟)内存2.2、地址窗口扩展 (AWE) 内存三、从 SQL Server 2012 (11.x) 开始发生的改变3.1、对内存...
    99+
    2023-04-19
    SQLServer的内存管理架构 SQL Server内存管理 SQL内存架构
  • C++的内存管理详细解释
    目录一、C/C++内存分布二、C语言中动态内存管理方式:1、malloc/calloc/realloc区别:三、C++中动态内存管理:new/delete四、实现原理五、面试常问问题...
    99+
    2022-11-12
  • C++动态内存管理详解
    目录1.C/C++程序地址空间2.C语言动态内存管理(1)malloc(2)calloc(3)realloc(4)free3.C++动态内存管理(1)C++为什么要设计一套自己专属的...
    99+
    2022-11-12
  • Java内存区域管理详解
    目录1 关于自动内存管理2 运行时数据区域2.1 程序计数器2.2 虚拟机栈2.2.1 局部变量表2.2.2 操作数栈2.3 本地方法栈2.4 堆2.5 方法区2.5.1 运行时常量...
    99+
    2022-11-13
  • C++内存管理详细解析
    目录一、C++内存管理1、 new/delete表达式2、new/delete重载3、类内自定义allocator(per-class allocator) 二、多线程内存分配器1、...
    99+
    2022-11-12
  • 详解Linux内核内存管理架构
    内存管理子系统可能是linux内核中最为复杂的一个子系统,其支持的功能需求众多,如页面映射、页面分配、页面回收、页面交换、冷热页面、紧急页面、页面碎片管理、页面缓存、页面统计等,而且对性能也有很高的要求。本文从内存管理硬...
    99+
    2022-06-04
    Linux 内存 架构
  • android内存管理机制是什么
    Android的内存管理机制是一种动态的机制,主要包括以下几个方面:1. 内存分配:Android使用堆来分配内存。堆是一个运行时数...
    99+
    2023-09-13
    android
  • 详解MySQL InnoDB存储引擎的内存管理
    存储引擎之内存管理 在InnoDB存储引擎中,数据库中的缓冲池是通过LRU(Latest Recent Used,最近最少使用)算法来进行管理的,即最频繁使用的页在LRU列表的最前段,而最少使用的页在LRU列表的尾端...
    99+
    2022-05-13
    MySQL InnoDB InnoDB 内存管理 InnoDB 存储引擎
  • Linux是如何管理内存的
    这篇文章主要讲解了“Linux是如何管理内存的”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Linux是如何管理内存的”吧!基本概念每个 Linux 进程都会有地址空间,这些地址空间由三个段...
    99+
    2023-06-15
  • C++内存管理如何理解
    这期内容当中小编将会给大家带来有关C++内存管理如何理解,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。前言;C++继承了C语言的指针,一直以来指针的一些问题困扰着开发人员,常见的指针问题主要有:内存泄露、...
    99+
    2023-06-26
  • Python中的内存管理之python list内存使用详解
    前言 使用 Python 的时候,我们知道 list 是一个长度可变对的数组, 可以通过 insert,append 和 extend 轻易的拓展其中的元素个数。 也可以使用运算符 ...
    99+
    2022-11-12
  • Python深入06——python的内存管理详解
    语言的内存管理是语言设计的一个重要方面。它是决定语言性能的重要因素。无论是C语言的手工管理,还是Java的垃圾回收,都成为语言最重要的特征。这里以Python语言为例子,说明一门动态类型的、面向对象的语言的...
    99+
    2022-06-04
    详解 内存管理 Python
  • iOS内存管理TaggedPointer使用原理详解
    目录正文Tagged Pointer 的原理MacOS 分析如何判断 Tagged Pointer Tagged Pointer 注意点正文 为了节省内存和提高执行效率,...
    99+
    2023-01-06
    iOS内存管理Tagged Pointer iOS Tagged Pointer
  • 详解Android的内存优化--LruCache
    概念: LruCache 什么是LruCache? LruCache实现原理是什么? 这两个问题其实可以作为一个问题来回答,知道了什么是 LruCache,就只然而然的知道 L...
    99+
    2022-06-06
    lrucache 内存优化 优化 Android
  • c++动态内存管理详解(new/delete)
    目录前言用法上对内置类型对自定义类型new/delete底层原理重载类的专属operator new和 operator delete定位newnew/delete与malloc/f...
    99+
    2022-11-13
  • Golang内存管理简单技巧详解
    目录引言预先分配切片结构中的顺序字段使用 map[string]struct{} 而不是 map[string]bool引言 除非您正在对服务进行原型设计,否则您可能会关心应用程序的...
    99+
    2022-11-11
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作