Android内存管理机制

1,205 阅读11分钟

一、概述

Android内存管理实质是对进程、对象、变量的内存分配和内存回收。

Application Framework和Linux内核负责进程内存的管理。Dalvik虚拟机负责对象和变量内存的管理。

二、进程的内存管理

Android对于进程内存的管理是由Application Framework和Linux内核负责的。

Application Framework 决定回收的进程类型,当进程空间紧张时,ActivityManagerService对所有进程进行评分(评分存放在变量adj中),更新评分到Linux内核,由Linux内核完成真正的内存回收(按进程优先级低->>高的顺序进行回收)。

(一) 低杀

在说进程之前,需要了解Android系统清理进程的机制--低杀。在Android中有一个心狠手辣的杀手,要想让我们的应用活下来,就要在开发应用时格外小心。不过我们也不用太担心,因为它只杀“坏蛋”,只要我们不使坏,那它就不会对我们下手。这个杀手叫低杀,它的全名是Low-Memory-Killer。 低杀跟垃圾回收器GC很像,GC的作用是保证应用有足够的内存可以使用,而低杀的作用是保证系统有足够的内存可以使用。GC会按照引用的强度来回收对象,而低杀会按照进程的优先级来回收资源,在这里进程优先级就相当于是应用被用户“引用”的强度。

(二) Android中有哪几种进程,是如何管理的?优先级

在Android中不同的进程有着不同的优先级,当两个进程的优先级相同时,低杀会优先考虑干掉消耗内存更多的进程。也就是如果我们应用占用的内存比其他应用少,并且处于后台时,我们的应用能在后台活下来,这也是内存优化为我们应用带来竞争力的一个直接体现。ActivityManagerService负责根据各种策略算法计算进程的adj值,然后交由系统内核进行进程的管理。

1、前台进程

用户当前操作所必需的进程。如果一个进程满足以下任一条件,即视为前台进程:

托管用户正在交互的Activity(已调用Activity的onResume() 方法) 托管某个Service,后者绑定到用户正在交互的 Activity 托管正在“前台”运行的 Service(服务已调用 startForeground()) 托管正执行一个生命周期回调的Service(onCreate()、onStart() 或 onDestroy()) 托管正执行其onReceive()方法的BroadcastReceiver

通常,在任意给定时间前台进程都为数不多。只有在内存不足以支持它们同时继续运行这一万不得已的情况下,系统才会终止它们。此时,设备往往已达到内存分页状态,因此需要终止一些前台进程来确保用户界面正常响应。

2、可见进程

没有任何前台组件、但仍会影响用户在屏幕上所见内容的进程。 如果一个进程满足以下任一条件,即视为可见进程:

托管不在前台、但仍对用户可见的 Activity(已调用其 onPause()方法)。例如,如果前台Activity 启动了一个对话框,允许在其后显示上一 Activity,则有可能会发生这种情况。 托管绑定到可见(或前台)Activity 的 Service。

可见进程被视为是极其重要的进程,除非为了维持所有前台进程同时运行而必须终止,否则系统不会终止这些进程。

3、服务进程

正在运行已使用 startService() 方法启动的服务且不属于上述两个更高类别进程的进程。尽管服务进程与用户所见内容没有直接关联,但是它们通常在执行一些用户关心的操作(例如,在后台播放音乐或从网络下载数据)。因此,除非内存不足以维持所有前台进程和可见进程同时运行,否则系统会让服务进程保持运行状态。

4、后台进程

包含目前对用户不可见的 Activity 的进程(已调用 Activity 的 onStop() 方法)。这些进程对用户体验没有直接影响,系统可能随时终止它们,以回收内存供前台进程、可见进程或服务进程使用。 通常会有很多后台进程在运行,因此它们会保存在 LRU (最近最少使用)列表中,以确保包含用户最近查看的 Activity 的进程最后一个被终止。如果某个 Activity 正确实现了生命周期方法,并保存了其当前状态,则终止其进程不会对用户体验产生明显影响,因为当用户导航回该 Activity 时,Activity 会恢复其所有可见状态。

5、空进程

不含任何活动应用组件的进程。保留这种进程的的唯一目的是用作缓存,以缩短下次在其中运行组件所需的启动时间。 为使总体系统资源在进程缓存和底层内核缓存之间保持平衡,系统往往会终止这些进程。

三、对象和变量的内存管理

Android对于对象、变量的内存策略同Java,内存管理由Dalvik/ART虚拟机负责分配和回收。

(一) Dalvik

要了解Android应用中对象和变量的内存管理机制,就先要了解承载着Android应用的虚拟机Dalvik,虽然Android5.0开始使用的是ART来承载应用的执行,但是ART也是基于Dalvik优化而来的。Dalvik是Dalvik Virtual Machine(Dalvik 虚拟机)的简称,是Android平台的核心组成部分之一。

1、Java虚拟机与Dalvik虚拟机的关系

Java 虚拟机是一个规范,由 Sun 公司制定,任何实现该规范的虚拟机都可以用来执行 Java 代码。比如说,你们公司对现有虚拟机的垃圾回收或者其他特性不满意,完全可以自己去按照 JVM 规范来开发一个自己的虚拟机,当然一般的公司或者个人几乎没有这么干的,毕竟这需要相当的技术实力或者业务场景。

但是Android就这样搞了。由于Androd运行在移动设备上,内存以及电量等诸多方面跟一般的PC设备都有本质的区别 ,一般的JVM没法满足移动设备的要求,所以在开发Android过程中,Android团队一开始就必须打造一个符合移动设备的可以执行Java代码的虚拟机,Dalvik虚拟机诞生了

2、Dalvik与JVM的区别

  • 1.架构:Dalvik基于寄存器,而JVM基于栈,很明显,基于寄存器的 Dalvik 在速度方面优势会更明显。

  • 2.执行的文件:Dallvik执行的是dex文件,JVM可执行的文件是.class文件。为什么Dalvik不执行.class文件呢?其实这是Android对Dalvik的优化,Java虚拟机JVM执行.class格式的字节码,每一个java文件对应一个.class的字节码文件,JVM在运行时为每个执行到的类装载字节码,而Android为了提高效率,在编译Android项目时,通过SDK提供的工具dex.jar会把所有的.class文件最终打包成一个.dex文件。

  • 3.运行环境:允许在有限内存中同时运行多个Dalvik虚拟机实例,并且每一个Dalvik应用作为一个独立的Linux进程执行。独立的进程可以防止在虚拟机崩溃的时候所有程序都被关闭,即使一个退出了也不会影响其他程序。Dalvik由Zygote孵化器创建,Zygote本身也是一个Dalvik虚拟机进程,当系统需要创建一个进程时,Zygote就会进行fork,快速创建和初始化一个Dalvik虚拟机实例。对于一些只读的系统库,所有的Dalvik 实例都能和Zygote共享一块内存区域,这样能节省内存开销。

3、Dalvik堆大小

每一个手机厂商都可以设定设备中每一个进程能够使用的堆大小,有关进程堆大小的值有下面三个。

(1)dalvik.vm.heapstartsize

堆分配的初始值大小,这个值越小,系统内存消耗越慢,但是当应用扩展这个堆,导致GC和堆调整时,应用会变慢。这个值越大,应用越流畅,但是可运行的应用也会相对减少。

(2)dalvik.vm.heapgrowthlimit

如果在清单文件中声明largeHeap为 true,则App使用的内存到heapsize才会OOM,否则达到 heapgrowthlimit就会OOM。

(3)dalvik.vm.heapsize

进程可用的堆内存最大值,一旦应用申请的内存超过这个值,就会OOM。

假如我们想看其中的一个值,我们可以通过命令查看,比如下面这条命令。
adb shell getprop dalvik.vm.heapsize

(二) ART

ART的全称是Android Runtime,在Android5.0后,使用了新的虚拟机ART全面替代了Dalvik。ART模式与Dalvik模式最大的不同在于,启用ART模式后,系统在安装应用的时候会进行一次预编译,将字节码转换为机器语言存储在本地,这样在运行程序时就不会每次都进行一次编译了,执行效率也大大提升,但也会占用更多的应用安装时间和存储空间。

1、Dalvik与ART的不同

(1)预编译

Dalvik中的应用每次运行时,需要通过即时编译器JIT(Just-In-Time)将字节码转换为机器码,这会使得应用的运行效率降低。在ART中,系统在安装应用时会进行一次预编译,采用预编译AOT(Ahead-Of-Time)将字节码成机器码并存储在本地,这样应用就不用在每次运行时执行编译了,运行效率也大大提高,但也会占用更多的应用安装时间和10%-20%的存储空间。

(2)垃圾回收

ART虚拟机在垃圾回收方面也比Dalvik更加高性能,据官方测试数据说gc效率提高2倍。 在Dalvik采用的垃圾回收算法是标记-清除算法,启动垃圾回收机制会造成两次暂停(一次在遍历阶段,另一次在标记阶段)。而在ART比Dalvik的GC速度要快,因为应用本身做了垃圾回收的一些工作,启动GC后不再是两次暂停,而是一次暂停,而且ART使用了一种新技术(packardpre-cleaning),在暂停前做了许多事情,减轻了暂停时的工作量。

(3)内存管理

Dalvik它的内存管理特点是:内存碎片化严重,当然这也是标记-清除算法(Mark and Sweep算法)带来的 弊端可以看出每次gc后内存千疮百孔,本来连续分配的内存块变得碎片化严重,之后再分配进入的对象再进行内存寻址变得困难。

在ART中,它将Java分了一块空间命名为Large-Object-Space,这块内存空间的引入用来专门存放large object,同时ART又引入了moving collector(移动收集器)的技术,即将不连续的物理内存块进行对齐 。对齐了后内存碎片化就得到了很好的解决。Large-Object-Space的引入一是因为moving collector对大块内存的位移时间成本太高,而且提高内存的利用率根官方统计,ART的内存利用率提高10倍了左右。

(4)CPU Dalvik是为32位CPU设计的,而ART支持64位并兼容32位CPU,这也是Dalvik被淘汰的主要原因。

2、总结

Dalvik其实可以理解为一个专为移动设备优化过的JVM,它的大部分地方都遵守了JVM规范,其实那些不符合规范的地方,就可以理解为为移动设备做的优化工作。而ART是一个具有更高性能的Android 虚拟机,从一开始就是为取代Dalvik而诞生的,它的AOT机制相比Dalvik的JIT机制使得应用有更快的启动速度(同时也会占用更多的应用安装时间和存储空间)。同时ART虚拟机在垃圾回收方面也比Dalvik更加高性能。