java虚拟机学习笔记(三)——不是虚拟机的安卓运行时

一、发展简述

1.1 初期——Dalvik

  在 Android 系统初期,不同于 Java 平台使用 JVM 加载字节码文件(.class),Android 系统由 Dalvik 担任虚拟机的角色,每次运行程序的时候,Dalvik 负责加载 dex/odex 文件并解析成机器码交由系统调用。Dalvik虚拟机是基于apache的java虚拟机,并被改进以适应低内存,低处理器速度的移动设备环境。

1.2 引入JIT

  为了适应硬件速度的提升,Android 系统系统也在不断更新,单一的 Dalvik 虚拟机已经渐渐地满足系统的要求了,2010 年 5 月 20 日,Google 发布 Android 2.2(Froyo冻酸奶),在这个版本中,Google 在 Android 虚拟中加入了 JIT 编译器(Just-In-Time Compiler)。
  Dalvik 使用 JIT 进行即时编译,借助 Java HotSpot VM,JIT 编译器可以对执行次数频繁的 dex/odex 代码进行编译与优化,将 dex/odex 中的 Dalvik Code(Smali 指令集)翻译成相当精简的 Native Code 去执行,JIT 的引入使得 Dalvik 的性能提升了 3~6 倍。

1.3 ART 诞生

  2013 年 10 月 31 日,Google 发布 Android 4.4(奇巧Kitkat),带来了全新的虚拟机运行环境 ART(Android RunTime)的预览版和全新的编译策略 AOT(Ahead-of-time),这个时候 ART 是和 Dalvik 共存的,用户可以在两者之间进行选择。
  2014 年 10 月 16 日,Google 发布 Android 5.0(棒棒糖Lollipop),ART 全面取代 Dalvik 成为 Android 虚拟机运行环境,至此,Dalvik 退出历史舞台,AOT 也成为唯一的编译模式。
  AOT 和 JIT 的不同之处在于:JIT 是在运行时进行编译,是动态编译,并且每次运行程序的时候都需要对 odex 重新进行编译;而 AOT 是静态编译,应用在安装的时候会启动 dex2oat 过程把 dex 预编译成 ELF 文件,每次运行程序的时候不用重新编译,是真正意义上的本地应用。

1.4 JIT回归

  在 Android 5.x 和 6.x 的机器上,系统每次 OTA 升级完成重启的时候都会有个应用优化的过程,这个过程就是刚才所说的 dex2oat 过程,这个过程比较耗时并且会占用额外的存储空间。
  2016 年 8 月 22 日,Google 发布 Android 7.0(牛轧糖Nougat),JIT 编译器回归,形成 AOT/JIT 混合编译模式,这种混合编译模式的特点是:
  应用在安装的时候 dex 不会被编译
  应用在运行时 dex 文件先通过解析器(Interpreter)后会被直接执行(这一步骤跟 Android 2.2 - Android 4.4之前的行为一致),与此同时,热点函数(Hot Code)会被识别并被 JIT 编译后存储在 jit code cache 中并生成 profile 文件以记录热点函数的信息。
手机进入 IDLE(空闲) 或者 Charging(充电) 状态的时候,系统会扫描 App 目录下的 profile 文件并执行 AOT 过程进行编译。
  可以看出,混合编译模式综合了 AOT 和 JIT 的各种优点,使得应用在安装速度加快的同时,运行速度、存储空间和耗电量等指标都得到了优化。

二、Dalvik和传统JVM的区别

  严格来说,Dalvik并不是java虚拟机,它并没有遵循JVM的实现规范。Dalvik与JVM在以下几个方面有明显不同:

  1. 架构
      java虚拟机基于栈,基于栈的机器必须使用指令来载入和操作栈上数据;Dalvik虚拟机基于寄存器。
  2. 字节码
      java虚拟机运行的是java字节码。(java类会被编译成一个或多个字节码.class文件,打包到.jar文件中,java虚拟机从相应的.class文件和.jar获取相应的字节码)。Dalvik运行的是自己专属的.dex字节码格式。(java类被编译成.class文件后,会通过一个dx工具将所有的.class文件转换成一个.dex文件,然后Dalvik虚拟机会从其中读取指令和数据)
  3. 进程模型
      JVM设计上是一个JVM运行多个java程序实例,然而DVM允许每个应用程序允许在自己独立的DVM中,每个DVM运行在独立的进程空间,放在了某个应用崩溃的时候影响到其他应用进程。
  4. 共享机制
      DVM有预加载-共享的机制,不用应用之间在运行时可以共享相同的类,拥有更高的效率。JVM不存在这种共享机制,不同的程序打包后是独立的。
  5. DVM早期没有使用JIT编译
      JVM使用了JIT编译器,而DVM早期没有使用JIT编译器。早期的DVM每次执行代码,都需要通过解释器将dex代码编译成机器码,然后交给系统处理,效率不是很高。从Android 2.2版本开始,DVM使用了JIT编译器,它会对多次运行的代码(热点代码)进行编译,生成相当精简的本地机器码(Native Code),这样在下次执行到相同逻辑的时候,直接使用编译之后的本地机器码,而不是每次都需要编译。但是应用程序每一次重新运行的时候,都要重做这个编译工作,因此每次重新打开应用程序,都需要JIT编译。

三、DVM和ART的区别

  ART是一种在Android操作系统上的运行环境,ART能够在第一次安装的时候,把应用程序的字节码转换为机器码。采用了预编译(AOT,Ahead-Of-Time)技术。DVM和ART的区别主要有4点:

  1. JIT与AOT
      DVM中的应用每次运行时,字节码都需要通过JIT编译器编译为机器码,这会使得应用程序的运行效率降低。而在ART中,系统在安装应用程序时会进行一次AOT(ahead of time compilation,预编译),将字节码预先编译成机器码并存储在本地,这样应用程序每次运行时就不需要执行编译了,运行效率会大大提升,设备的耗电量也会降低。
      采用AOT也会有缺点,主要有两个:第一个是AOT会使得应用程序的安装时间变长,尤其是一些复杂的应用,第二个是字节码预先编译成机器码,机器码需要的存储空间会多一些。为了解决上面的缺点,Android 7.0版本中的ART加入了即时编译器JIT,作为AOT的一个补充,在应用程序安装时并不会将字节码全部编译成机器码,而是在运行中将热点代码编译成机器码,从而缩短应用程序的安装时间并节省了存储空间。
  2. 处理位数
      DVM是为32位CPU设计的,而ART支持64位并兼容32位CPU,这也是DVM被淘汰的主要原因之一。
  3. 垃圾回收
      ART对垃圾回收机制进行了改进,比如更频繁地执行并行垃圾收集,将GC暂停由2次减少为1次等。
  4. 内存划分
      ART的运行时堆空间划分和DVM不同。

由此可以总结出ART的优缺点:

ART优缺点

四、垃圾回收

4.1 Dalvik垃圾回收

  Dalvik虚拟机比其他Java虚拟机中的垃圾收集要简单一些, 因为没有进行内存整理(no compacting)。也就是说堆内存中的对象在创建之后其地址永远都不会发生改变, 使得虚拟机其余部分的实现变得相对简单。

4.1.1 DVM运行时堆

  ​Android 4.x中DVM运行时堆使用的是标记-清除算法进行GC。DVM的运行时堆包括两个Space和一些辅助数据结构:

DVM运行时堆

4.1.2 引起DVM发生GC的原因

  有以下几种:

  1. GC_CONCURRENT:当堆开始填充时,并发GC可以释放内存。

2 .GC_POR_MALLOC:当堆内存已满时,App尝试分配内存而引起的GC,系统必须停止App并回收内存。

  1. GC_HPROF_DUMP_HEAP:当请求创建HPROF文件来分析堆内存时出现的GC。

  2. GC_EXPLICIT:显式的GC,例如调用System.gc()(应该避免调用显式的GC,信任GC会在需要时运行)。

  3. GC_EXTERNAL_ALLOC:仅适用于API级别小于等于10,且用于外部分配内存的GC。

4.2 ART垃圾回收

  ​与DVM的GC不同的是,ART 采用了多种垃圾收集方案,每个方案会运行不同的垃圾收集器,默认是采用了CMS(Concurrent Mark-Sweep)方案,该方案主要使用了sticky-CMS和partial-CMS。根据不同的CMS方案,ART的运行时堆的空间也会有不同的划分。

4.2.1 ART运行时堆

  默认的ART运行时堆由4个Space和多个辅助结构组成:

ART运行时堆

  采用标记-清除算法时。DVM和ART的空间划分如下:

空间划分

4.2.2 引起ART发生GC的原因

  有以下几种:

  1. Concurrent:并发GC,不会使App的线程暂停,该GC是在后台线程运行的,并不会阻止内存分配。
  2. Alloc:当堆内存已满时,App尝试分配内存而引起的GC,这个GC会发生在正在分配内存的线程中。
  3. Explicit:App显示的请求垃圾收集,例如调用System.gc()。与DVM一样,最佳做法是应该信任GC并避免显式地请求GC,显式地请求GC会阻止分配线程并不必要地浪费CPU周期。如果显式地请求GC导致其他线程被抢占,那么有可能会导致jank(App同一帧画了多次)。
  4. NativeAlloc:Native内存分配时,比如为Bitmaps或者RenderScript分配对象,这会导致Native内存压力,从而触发GC。
  5. CollectorTransition:由堆转换引起的回收,这是运行时切换GC而引起的。收集器转换包括将所有对象从空闲列表空间复制到碰撞指针空间(反之亦然)。当前,收集器转换仅在以下情况下出现:在内存较小的设备上,App 将进程状态从可察觉的暂停状态变更为可察觉的非暂停状态(反之亦然)。
  6. HomogeneousSpaceCompact:齐性空间压缩是指空闲列表到压缩的空闲列表空间,通常发生在当App已经移动到可察觉的暂停进程状态时。这样做的主要原因是减少了内存使用并对堆内存进行碎片整理。
  7. DisableMovingGc:不是真正触发GC的原因,发生并发堆压缩时,由于使用了GetPrimitiveArrayCritical,收集会被阻塞。在一般情况下,建议不要使用GetPrimitiveArrayCritical,因为它在移动收集器方面具有限制。
  8. HcapTrim:不是触发GC的原因,但是请注意,收集会一直被阻塞,直到堆内存整理完毕。

五、小结

  本文简要描述了安卓中虚拟机的发展,其实目前来看,ART的名字是非常贴切的,DVM开始具备了非虚拟机的特点,而ART则完全无法仅仅用虚拟机去描述,ART为安卓应用提供了一整套的运行时环境,而不是作为虚拟机去执行指令。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,793评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,567评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,342评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,825评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,814评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,680评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,033评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,687评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,175评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,668评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,775评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,419评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,020评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,206评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,092评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,510评论 2 343

推荐阅读更多精彩内容