专栏首页Android开发实战Android的内存管理机制

Android的内存管理机制

Android使用虚拟内存和分页,不支持交换

垃圾收集

无论是ART还是Dalvik虚拟机,都和众多Java虚拟机一样,属于一种托管内存环境(程序员不需要显示的管理内存的分配与回收,交由系统自动管理)。托管内存环境会跟踪每个内存分配, 一旦确定程序不再使用一块内存,它就会将其释放回堆中,而无需程序员的任何干预。回收托管内存环境中未使用内存的机制称为垃圾回收。

垃圾收集有两个目标:

  • 在程序中查找将来无法访问的数据对象;
  • 回收这些对象使用的资源。

Android的垃圾收集器不带压缩整理功能(Compact),即不会对Heap做碎片整理。

Android的内存堆是分代式(Generational)的,意味着它会将所有分配的对象进行分代,然后分代跟踪这些对象。例如,最近分配的对象属于年轻代(Young Generation)。当一个对象长时间保持活动状态时,它可以被提升为年老代(Older Generation),之后还能进一步提升为永久代(Permanent Generation)。

每一代的对象可占用的内存总量都有其专用上限。每当一代开始填满时,系统就会执行垃圾收集事件以试图释放内存。垃圾收集的持续时间取决于它在收集哪一代的对象以及每一代中有多少活动对象。

虽然垃圾收集速度非常快,但它仍然会影响应用程序的性能。通常情况下你不需要控制代码中何时执行垃圾收集事件。系统有一组用于确定何时执行垃圾收集的标准。满足条件后,系统将停止执行当前进程并开始垃圾回收。如果在像动画或音乐播放这样的密集处理循环中发生垃圾收集,则会增加处理时间。这种增加可能会导致你的应用程序中的代码执行超过建议的16ms阈值。

为实现高效,流畅的帧渲染,Android建议绘制一帧的时间不要超过16ms。

此外,你的代码可能会执行各种工作,这些工作会导致垃圾收集事件更频繁地发生,或使其持续时间超过正常范围。例如,如果在Alpha混合动画的每个帧期间在for循环的最内部分配多个对象,则大量的对象就会污染内存堆。此时,垃圾收集器会执行多个垃圾收集事件,并可能降低应用程序的性能。

共享内存

Android可以跨进程共享RAM页面(Pages)。它可以通过以下方式实现:

  • 每个应用程序进程都是从名为Zygote的现有进程分叉(fork)出来的。Zygote进程在系统引导并加载framework代码和资源(例如Activity Themes)时启动。要启动新的应用程序进程,系统会fork Zygote进程,然后在新进程中加载并运行应用程序的代码。这种方法允许在所有应用程序进程中共享大多数的为framework代码和资源分配的RAM页面。
  • 大多数静态数据都被映射到一个进程中。该技术允许在进程之间共享数据,并且还允许在需要时将其Page out。这些静态数据包括:Dalvik代码(通过将其置于预链接的.odex文件中进行直接的memory-mapping),app资源(通过将资源表设计为可以mmap的结构并通过对齐APK的zip条目) 和传统的项目元素,如.so文件中的本地代码。
  • 在许多地方,Android使用显式分配的共享内存区域(使用ashmem或gralloc)在进程间共享相同的动态RAM。例如,Window surface在应用程序和屏幕合成器之间使用共享内存,而游标缓冲区在Content Provider和客户端之间使用共享内存。

分配和回收应用的内存

Android为每个进程分配内存的时候,采用了弹性分配方式,也就是刚开始并不会一下分配很多内存给每个进程,而是给每一个进程分配一个“够用”的虚拟内存范围。这个范围是根据每一个设备实际的物理内存大小来决定的,并且可以随着应用后续需求而增加,但最多也只能达到系统为每个应用定义的上限。

堆的逻辑大小与其使用的物理内存总量并不完全相同。在检查应用程序的堆时,Android会计算一个名为“比例集大小”(PSS)的值,该值会考虑与其他进程共享的脏页面和干净页面,但其总量与共享该RAM的应用程序数量成正比。此PSS总量就是系统认为是你的物理内存占用量。

Android会在内存中尽量长时间的保持应用进程,即使有些进程不再使用了。这样,当用户下次启动应用的时候,只需要恢复当前进程就可以了,不需要重新创建进程,进而减少应用的启动时间。只有当Android系统发现内存不足,而其他为用户提供更紧急服务的进程又需要内存时,Android就会决定关闭某些进程以回收内存。关于这部分内容,稍后再细说。

限制应用的内存

为了维护高效的多任务环境,Android为每个应用程序设置了堆大小的硬性限制。该限制因设备而异,取决于设备总体可用的RAM。如果应用程序已达到该限制并尝试分配更多内存,则会收到 OutOfMemoryError 。

在某些情况下,你可能希望查询系统以准确确定当前设备上可用的堆空间大小,例如,确定可以安全地保留在缓存中的数据量。你可以通过调用 getMemoryClass() 来查询系统中的这个数字。此方法返回一个整数,指示应用程序堆可用的兆字节数。

切换应用

当用户在应用程序之间切换时,Android会将非前台应用程序(即用户不可见或并没有运行诸如音乐播放等前台服务的进程)缓存到一个最近最少使用缓存(LRU Cache)中。例如,当用户首次启动应用程序时,会为其创建一个进程; 但是当用户离开应用程序时,该进程不会退出。系统会缓存该进程。如果用户稍后返回应用程序,系统将重新使用该进程,从而使应用程序切换更快。

如果你的应用程序具有缓存进程并且它保留了当前不需要的内存,那么即使用户未使用它,你的应用程序也会影响系统的整体性能。当系统内存不足时,就会从最近最少使用的进程开始,终止LRU Cache中的进程。另外,系统还会综合考虑保留了最多内存的进程,并可能终止它们以释放RAM。

当系统开始终止LRU Cache中的进程时,它主要是自下而上的。系统还会考虑哪些进程占用更多内存,因为在它被杀时会为系统提供更多内存增益。因此在整个LRU列表中消耗的内存越少,保留在列表中并且能够快速恢复的机会就越大。

本文分享自微信公众号 - 喘口仙氣(gh_db8538619cdd),作者:Anymarvel

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2020-09-04

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Android内存管理(八)内存管理小结

    今天从操作系统的角度来闲聊一下代码开发过程中如何配合系统做内存管理。内存就是一块数据存储区域,是可被操作系统调度的资源。在多任务(进程)的OS中,内存管理尤为重...

    Anymarvel
  • Android O 行为变更官方指南

    Android O 除了提供诸多新特性和功能外,还对系统和 API 行为做出了各种变更。本文重点介绍您应该了解并在开发应用时加以考虑的一些主要变更。

    Anymarvel
  • Android对Linux系统的内存管理机制进行的优化

    Android对内存的使用方式同样是“尽最大限度的使用”,这一点继承了Linux的优点。只不过有所不同的是,Linux侧重于尽可能多的缓存磁盘数据以降低磁盘IO...

    Anymarvel
  • [编程经验]C语言free释放内存后为什么指针里的值不变?竟然还可以输出?

    今天你家范儿给大家带来一个的东西——关于C语言为什么释放指针后,指向这块内存的指针的值不变问题的编程经验!!行了,咱们话不多少,直接上主食。 ...

    编程范 源代码公司
  • Android NDK and OpenCV development with Android Studio

    Android NDK and OpenCV development with Android Studio

    宅男潇涧
  • iOS开发笔记(一)

    前言 iOS开发笔记(一) iOS开发笔记(二) iOS开发笔记(三) iOS开发笔记(四) 《开发笔记》系列记录一些开发中遇到的问题以及思考。 本文主...

    落影
  • 去掉EMLOG分类url中的sort字样

    前言:EMLOG使用伪静态后,分类URL网址中有个sort字样,都想把这个sort去掉;想去除EMLOG伪静态分类网址中的sort,进入include文件夹下,...

    陌涛
  • SAP UI5里Batch操作和Read操作的区别

    I would like to share with you my story today about fighting with one customer i...

    Jerry Wang
  • 【java工具类】xml和hashmap的相互转换

    用户5640963
  • 七天学会ASP.NET MVC (一)——深入理解ASP.NET MVC

    ? 系列文章 七天学会ASP.NET MVC (一)——深入理解ASP.NET MVC 七天学会ASP.NET MVC (二)——ASP.NET MVC 数据...

    葡萄城控件

扫码关注云+社区

领取腾讯云代金券