0%

第11章:Dalvik 和 ART

Dalvik虚拟机

Dalvik虚拟机 简称 DVM,第10章提到,它不是一个 jvm,主要原因是它没有遵循jvm规范实现,二者的主要区别有:

  • 基于的架构不同

JVM 基于栈,DVM 基于寄存器。基于栈意味着需要去栈中读写数据,所需要的指令更多,并且速度更慢,对于性能优先的移动设备,显然是不合适的。

  • 执行的字节码不同

在Java SE 程序中,Java 类被编译成一个或者多个.class 文件,并被打包成 jar 文件,之后JVM 会通过相应的 .class 和 jar 文件获取相应的字节码;而DVM 会用dx工具将所有的 .class 文件转换为一个 .dex 文件,然后DVM 从该 .dex 文件中读取指令和数据。
.jar文件里面包含多个.class 文件,每个.class文件包含了该类的常量池、类信息、属性等,当JVM 加载.jar文件的时候,会加载里面所有的.class文件,JVM 这种加载方式很慢(首先就是很多io操作),对于内存有限的移动设备并不合适;而.dex文件将所有的.class里面所包含的信息全部整合到一块了,这样再加载就减少了I/O操作,加快查找速度;并且,相对Android而言,.class中有许多冗余信息,dex工具会去掉冗余信息。

DVM与JVM执行的字节码不同

  • DVM 允许在有限的内存中同时运行多个进程

在Android中,每个应用都运行在一个DVM中,每个DVM 实例都运行在一个独立的进程中,这样,某一个虚拟机崩溃的时候不会导致其他app也关闭。

  • DVM 由Zygote创建和初始化

在第2章有介绍Zygote,它是一个DVM进程,当系统需要创建一个应用程序时,Zygote就会fork自身,快速地创建和初始化一个DVM实例。对于一些只读的库,所有的DVM实例都会和Zygote共享一块内存区域,节省内存开销。

  • DVM 有共享机制

DVM 的共享机制可以使不同应用之间在运行时可以共享相同的类,这带来更高的效率;而JVM不具有这种机制,不同的程序,打包以后的程序都是彼此独立的,即便它们在包里使用了同样的类,运行时也是单独加载和运行的。

DVM 运行时堆

DVM运行时堆使用标记-清除算法进行GC,它由两个Space以及多个辅助数据结构组成,两个Space分别是:Zygote Space(Zygote Heap) 和 Allocation Space(Active Heap),前者用于管理Zygote进程在启动过程中预加载和创建的对象,并且Zygote Space不会触发GC,Zygote 进程和应用进程之间会共享Zygote Space。在Zygote 进程fork第一个子进程前,会把Zygote Space 分为两部分,原来已经被使用的部分对仍旧叫做Zygote Space,而未使用的那部分堆叫做 Allocation Space ,以后的对象都会在 Allocation Space上进行分配和释放。

ART虚拟机

Android 4.4 的时候发布了ART虚拟机,但是4.4版本默认还是使用DVM,Android 5.0 及以后版本默认采用ART,从此,DVM退出历史舞台。

ART 与 Dalvik 区别

ART 与 Dalvik 的区别主要有4点:

  • DVM 是为32位CPU涉及的,而ART支持64位并且兼容32位 CPU。
  • ART 对垃圾回收机制进行了改进,将 GC 暂停由2次减少为1次,并且频繁执行并行垃圾收集。
  • ART 的运行时堆空间划分和DVM不同。
  • DVM中应用每次运行时,字节码通过 JIT 编译器编译为机器码,使得应用程序运行效率低下;而在ART中,系统在安装应用程序时会进行一次AOT(ahead of time compilation,预编译),将字节码预先编译成机器码并存储在本地,这样应用程序每次运行时就不需要执行编译了。

ART也有两个主要缺点:一是AOT使应用安装时间变长,二是预编译的机器码占用的存储空间比较大。为了解决上面的缺点,Android 7.0 版本在ART中加入了JIT编译器,作为AOT的补充:在安装应用时不会将字节码全部编译成机器码,而是在运行中将热点代码编译器机器码,以达到缩短应用安装时间并节省存储空间。

更详细的内容,可以参考官网上的描述

ART 运行时堆

与DVM 的GC不同的是,ART采用多种垃圾收集方案,每个方案会运行不同的垃圾收集器,默认是采用 CMS(Concurrent Mark-Sweep)方案,主要有sticky-CMS 和 partial-CMS,不同的CMS方案,ART运行时堆得空间划分也不同,默认由4个Space和多个辅助结构组成,采用标记-清除算法时,两种虚拟机运行时堆对比图如下:

两种虚拟机运行时堆对比

由图可以看到ART的4个Space,其中,Zygote Space 、Allocation Space 和 DVM 中的作用一样,ImageSpace 用来存放一些预加载类,Large Object Space 用来分配一些大对象。其中Zygote Space 与 Image Space 是进程共享的。

ART 的GC 日志

  1. GC Reason
    ART 虚拟机GC日志中会包含产生这次GC原因,主要会有:
  • Concurrent: 并发GC,在后台线程运行GC,不会使App的线程暂停,不会阻止内存分配。
  • Alloc: 当堆内存已满,App尝试分配内存而引起的GC,这个GC发生在正在分配内存的线程中。
  • Explicit: App显式请求垃圾收集,比如 System.gc()。
  • NativeAlloc: Native 内存分配时触发的GC。
  1. 垃圾收集器名称
    ART 虚拟机GC日志中会包含所使用的收集器名称,主要会有:
  • Concurrent Mark Sweep(CMS): 它是以最短收集暂停时间为目标的收集器,采用标记-清除算法,能释放除了Image Space外的所有空间
  • Concurrent Partial Mark Sweep: 能释放除了Image Space 和 Zygote space 意外的所有空间
  • Concurrent Sticky Mark Sweep: 粘性收集器,基于分代的垃圾收集思想,只能释放自上次GC以来分配的对象,这个收集器扫描比较频繁,因为它很快并有很短的暂停时间。
  • Marksweep + Semispace:非并发GC,复制GC用于堆转换以及堆碎片整理。

DVM 和 ART 的诞生

它们是从Zygote进程诞生的,这样,Zygote进程就持有了DVM或者ART的实例,此后,Zygote进程每次fork自身创建新的应用进程时,应用程序进程也就得到了 DVM 或者ART 的实例,即每个应用进程都有一个单独的虚拟机实例。这样做的好处是,无需在每次启动应用程序进程时都要创建DVM或者ART,从而加快了应用程序进程的启动速度

谢谢你的鼓励