Android 内存管理系列

本章为官方文档,主要介绍 Android 如何管理应用进程和内存分配。

首先了解为什么要有内存优化,官方的文档介绍

https://developer.android.com/topic/performance/memory-overview?hl=zh-cn

Android 运行时 (ART) 和 Dalvik 虚拟机使用分页和内存映射来管理内存。这意味着应用修改的任何内存,无论修改的方式是分配新对象还是轻触内存映射的页面,都会一直驻留在 RAM 中,并且无法换出。要从应用中释放内存,只能释放应用保留的对象引用,使内存可供垃圾回收器回收。这种情况有一个例外:对于任何未经修改的内存映射文件(如代码),如果系统想要在其他位置使用其内存,可将其从 RAM 中换出。

https://developer.android.com/topic/performance/memory?hl=zh-cn

随机存取存储器 (RAM) 在任何软件开发环境中都是一项宝贵资源,但在移动操作系统中,由于物理内存通常都有限,因此 RAM 就更宝贵了。虽然 Android 运行时 (ART) 和 Dalvik 虚拟机都执行例行的垃圾回收任务,但这并不意味着您可以忽略应用分配和释放内存的位置和时间。您仍然需要避免引入内存泄漏问题(通常因在静态成员变量中保留对象引用而引起),并在适当时间(如生命周期回调所定义)释放所有 Reference 对象。

Android通过的以下几个方面对内存进行管理:

  • 垃圾回收

    ART 或 Dalvik 虚拟机之类的受管内存环境会跟踪每次内存分配。一旦确定程序不再使用某块内存,它就会将该内存重新释放到堆中,无需程序员进行任何干预。这种回收受管内存环境中的未使用内存的机制称为“垃圾回收”。垃圾回收有两个目标:在程序中查找将来无法访问的数据对象,并回收这些对象使用的资源。

    Android 的内存堆是分代的,这意味着它会根据分配对象的预期寿命和大小跟踪不同的分配存储分区。例如,最近分配的对象属于“新生代”。当某个对象保持活动状态达足够长的时间时,可将其提升为较老代,然后是永久代。

    堆的每一代对相应对象可占用的内存量都有其自身的专用上限。每当一代开始填满时,系统便会执行垃圾回收事件以释放内存。垃圾回收的持续时间取决于它回收的是哪一代对象以及每一代有多少个活动对象。

    尽管垃圾回收速度非常快,但仍会影响应用的性能。通常情况下,您无法从代码中控制何时发生垃圾回收事件。系统有一套专门确定何时执行垃圾回收的标准。当条件满足时,系统会停止执行进程并开始垃圾回收。如果在动画或音乐播放等密集型处理循环过程中发生垃圾回收,则可能会增加处理时间,进而可能会导致应用中的代码执行超出建议的 16ms 阈值,无法实现高效、流畅的帧渲染。

    此外,您的代码流执行的各种工作可能迫使垃圾回收事件发生得更频繁或导致其持续时间超过正常范围。 例如,如果您在 Alpha 混合动画的每一帧期间,在 for 循环的最内层分配多个对象,则可能会使内存堆受到大量对象的影响。在这种情况下,垃圾回收器会执行多个垃圾回收事件,并可能降低应用的性能。

  • 共享内存

    为了在 RAM 中容纳所需的一切,Android 会尝试跨进程共享 RAM 页面。它可以通过以下方式实现这一点:

    每个应用进程都从一个名为 Zygote 的现有进程分叉。系统启动并加载通用框架代码和资源(如 Activity 主题背景)时,Zygote 进程随之启动。为启动新的应用进程,系统会分叉 Zygote 进程,然后在新进程中加载并运行应用代码。这种方法使为框架代码和资源分配的大多数 RAM 页面可在所有应用进程之间共享。 大多数静态数据会内存映射到一个进程中。这种方法使得数据不仅可以在进程之间共享,还可以在需要时换出。静态数据示例包括:Dalvik 代码(通过将其放入预先链接的 .odex 文件中进行直接内存映射)、应用资源(通过将资源表格设计为可内存映射的结构以及通过对齐 APK 的 zip 条目)和传统项目元素(如 .so 文件中的原生代码)。 在很多地方,Android 使用明确分配的共享内存区域(通过 ashmem 或 gralloc)在进程间共享同一动态 RAM。例如,窗口 surface 使用在应用和屏幕合成器之间共享的内存,而光标缓冲区则使用在内容提供器和客户端之间共享的内存。

  • 分配与回收应用内存

    Dalvik 堆局限于每个应用进程的单个虚拟内存范围。这定义了逻辑堆大小,该大小可以根据需要增长,但不能超过系统为每个应用定义的上限。

    堆的逻辑大小与堆使用的物理内存量不同。在检查应用堆时,Android 会计算按比例分摊的内存大小 (PSS) 值,该值同时考虑与其他进程共享的脏页和干净页,但其数量与共享该 RAM 的应用数量成正比。此 (PSS) 总量是系统认为的物理内存占用量。有关 PSS 的详情,请参阅调查 RAM 使用量指南。

    Dalvik 堆不压缩堆的逻辑大小,这意味着 Android 不会对堆进行碎片整理来缩减空间。只有当堆末尾存在未使用的空间时,Android 才能缩减逻辑堆大小。但是,系统仍然可以减少堆使用的物理内存。垃圾回收之后,Dalvik 遍历堆并查找未使用的页面,然后使用 madvise 将这些页面返回给内核。因此,大数据块的配对分配和解除分配应该使所有(或几乎所有)使用的物理内存被回收。但是,从较小分配量中回收内存的效率要低得多,因为用于较小分配量的页面可能仍在与其他尚未释放的数据块共享。

  • 限制应用内存

    为了维持多任务环境的正常运行,Android 会为每个应用的堆大小设置硬性上限。不同设备的确切堆大小上限取决于设备的总体可用 RAM 大小。如果您的应用在达到堆容量上限后尝试分配更多内存,则可能会收到 OutOfMemoryError。

    在某些情况下,例如,为了确定在缓存中保存多少数据比较安全,您可能需要查询系统以确定当前设备上确切可用的堆空间大小。您可以通过调用 getMemoryClass() 向系统查询此数值。此方法返回一个整数,表示应用堆的可用兆字节数。

  • 切换应用

    当用户在应用之间切换时,Android 会将非前台应用保留在缓存中。非前台应用就是指用户看不到或未运行前台服务(如音乐播放)的应用。例如,当用户首次启动某个应用时,系统会为其创建一个进程;但是当用户离开此应用时,该进程不会退出。系统会将该进程保留在缓存中。如果用户稍后返回该应用,系统就会重复使用该进程,从而加快应用切换速度。

    如果您的应用具有缓存的进程且保留了目前不需要的资源,那么即使用户未使用您的应用,它也会影响系统的整体性能。当系统资源(如内存)不足时,它将会终止缓存中的进程。系统还会考虑终止占用最多内存的进程以释放 RAM。

以上内容为官方内容,可从官网查阅:

Android 系统强调了内存释放的重要性并且建议从以下几点优化应用内存使用:

  • 监控可用内存和内存使用量
  • 使用内存效率更高的代码结构
  • 移除会占用大量内存的资源和库

本次Android内存管理以系列课程为体系,今天为Android内存管理第一章.敬请期待

results matching ""

    No results matching ""