在 Android 操作系统中,有一个非常重要的核心部分: Android Runtime。说到这个,我相信很多人都听到过 DalvikARTJIT 以及 AOT。或许好多人也和我之前一样,并不了解这些名词,以及这些名词背后做了些什么事情。本文从笔者了解到的信息,记录了 Android Runtime 中设计的一些概念,以及应用。

1. 虚拟机

在了解上面提到的名词之前,我们需要先知道什么是虚拟机, 它和 Android 又有什么样的渊源。

在 2017 年的 Google I/O 大会上,Google 正式宣布 Kotlin-fist,Kotlin 正式成为 Android 的主要开发语言。在这之前, Android 开发者们都是使用 Java 来进行 Android 应用程序开发的。 Kotlin 和 Java 都是基于 JVM 的 CLASS格式 实现的语言,开发者通过他们编写出来的源代码都会被编译成 CLASS 文件。在 Android 中,也有相似的流程。

“一次编写,到处运行” 是 Java 的宣传口号,而 JVM 正是实现此目标的关键所在。 JVM 即 Java 虚拟机,它将物理机器中的资源(CPU、内存、输入输出等)进行抽象 ,实现相同的代码,可以在不同的硬件资源上进行执行。

在 Android 开发中,我们使用 Java/Kotlin 写的代码,会先编译成 class 文件,在通过 dx 工具转换成 classes.dex,运行时,在 Android 系统中,也使用了虚拟机来执行 APK 文件中的代码的。

2. Dalvik

在 Android 5.0 以前,Android Runtime 叫 Dalvik, 用于 Android 运行 APK 的虚拟机,开发者写的所有代码都是通过它进行执行的。

DVM 的基本思想与 JVM 大体相同,但是在早期,Android 的智能手机只有很小的内存,因此 DVM 被设计用于手机端时,进行了很多独有的优化措施,其中最主要的目标就是减少内存使用。

DVM是基于寄存器架构(register-based) ,JVM 是基于堆栈的架构(stack-based)。

因此, Android APP 在运行的时候,不会将所有的代码都进行编译成机器码,而当代码需要运行的时候,才会去进行小范围的编译,这种方式被叫做 JIT (Just In Time compilation)。这种方式有点像解释器模式,能够节约大量的内存出来,让应用程序能够正常执行。但是,这也对应用程序运行时的性能带来一定的影响。当然,为了提高性能,DVM 也会将经常用到的代码缓存下来,降低重复编译引起的性能消耗。

随着 Android 手机的发展,内存也越来越大,内存已经不再是限制。并且,各大应用厂商开发的应用程序体积也越来越大,JIT 编译带来的性能瓶颈愈来愈明显。因此, 在 Android 5.0 过后,就不再使用 Dalvik 作为 Android 的运行环境了。

3. ART

在 Android 5.0 及以后,Google 使用 ART 替换了 Dalvik,使用新的 Android Runtime。 ART 在设计上与 Dalvik完全不同,它引入了 AOT (Ahead of Time compilation)模式,在应用程序安装的时候,AOT 的过程是使用系统自带的 dex2oat 工具将 APK 中的 dex 文件进行编译,将编译出来的机器码放入磁盘空间中,应用程序运行的时候,直接运行,极大的提高了性能,但也引起了新的问题,应用的安装时间会变得更长,也需要更大的磁盘空间来进行存储。 还依稀记得 2015 的时候,好多人还在为安装新的应用程序而把不常用的应用卸载掉。

在国内,有很多插件化框架,通过插件化框架,可以从网络上动态下载代码来实现应用程序运行时更新。 在 RePlugin 中,下载下来的插件也是 APK 文件,在 RePlugin 的代码中,就有对 dex 文件进行编译的处理逻辑,感兴趣的同学可以去看看。

这种 AOT 的模式,一直持续到 Android 6 的系统,在 Android 7.0 后,又进行了一次较大的升级。

4. Hybrid: AOT + JIT

从前面可以看到, AOT 和 JIT 的优缺点是反过来的,因此 Google 将这两种方式进行了结合,搞了一个混合编译的方案。ART 下的 JIT 架构如下:

从整个流程可以看到, 未经过编译的代码,如果是非热点代码,会直接进行解释执行,如果是热点代码,会经过 JIT 编译成机器码,再进行执行。JIT编译而来的机器码是存储到内存中的,不是在硬盘上。所以,在应用重新启动时,所有的热点代码也都需要使用 JIT 重新编译成机器码。代码在整个 ART 虚拟机中的执行流程如下图所示:

在上图中,可以看到,没有经过编译的函数调用,在首次执行的时候,会直接使用 JIT 解释执行,并且会对相应的代码执行进行采样,统计出热点代码,并将统计出来的信息存放到 /data/misc/profiles/cur/0/packageName/primary.prof 下,这个文件有什么用呢? 在上图中,可以看到了 JIT 的整个运行流程,但没有 .oat 文件编译生成的流程。既然是 AOT 与 JIT 混合模式,那肯定还是有 AOT 相关的流程, AOT 编译的守护进程会在设备空闲以及充电的时候,使用生成的 primary.prof 文件进行部分代码编译。

5. Baseline Profiles

在前面的流程中,已经可以知道 Profile 的配置信息,可以帮助 AOT 编译,提高应用程序性能。但是,应用程序需要在本地运行一段时间后,才能统计出热点代码。而 Baseline Profiles 的原理就是让开发者自己将热点代码统计出来生成配置文件,在 APP 运行的时候,将配置写入到指定位置,帮助 AOT 编译出热点代码的机器码。更好地提高使用效率。

  1. 生成 profile

按照官方文档,可以使用 Jetpack 当中的 Macrobenchmark,生成 Baseline Profile 配置文件,按如下代码就可以生成了:

@ExperimentalBaselineProfilesApi
@RunWith(AndroidJUnit4::class)
class BaselineProfileGenerator {@get:Rule val baselineProfileRule = BaselineProfileRule()@Testfun startup() =baselineProfileRule.collectBaselineProfile(packageName = "com.example.app") {pressHome()// This block defines the app's critical user journey. Here we are interested in// optimizing for app startup. But you can also navigate and scroll// through your most important UI.startActivityAndWait()}
}

PS: 需要一台 Android 9.0 及以上,开启 userdebug 或者 root 过的设备。

  1. 接入 ProfileInstaller
dependencies {implementation("androidx.profileinstaller:profileinstaller:1.2.0-beta01")
}

PS: com.android.tools.build:gradle 需要使用 7.1.0-alpha01 及以上的版本,官方推荐使用 7.3.0-beta01 以上的版本。

  1. 将第一步生成的 proflie 文件重命名为 baseline-prof.txt 放入到 src/main 目录下,与 AndroidManifest.xml 文件统计。

简单几步就能使用 Baseline Profile能力了。让应用程序运行性能更好。


以上为 Android 操作系统中虚拟机的进化过程,以及开发者可以在加速 APP 运行可以做的事情。

这么多年, Android 虚拟机到底做了些什么?相关推荐

  1. python中mod是什么意思_【python中,mod_python到底做了些什么呢?】mod python 教程

    python 编程小白 ,不会用doctest 请大神指教怎么用!! >>> >>> def is_between(v, lower, higher): ...   ...

  2. 软件在安装时,到底做了些什么?

    软件在安装时,到底做了些什么? 大家每天都在用电脑,可能也经常在自己的电脑上安装软件.就算自己没安装过,至少也看到人家安装过软件.在这里,我不是想教你怎么安装软件,而是想向你展示,软件在安装的过程中, ...

  3. 一个菜鸟的暑期到底做了些什么

    一个菜鸟的暑期都做了些什么 linux虚拟机的搭建 PhotoShop的学习 暑期主攻:Web前端 总结 linux虚拟机的搭建 先从最简单的开始说起,依据网上的流程以及学长的帮助,搭建好了虚拟机,但 ...

  4. 什么是spring框架?spring框架到底有什么用?spring框架到底做了些什么?

    什么是spring框架,spring框架究竟有什么用呢?我们可以用spring框架来做些什么呢?这是我今天要说的内容. 当然,百度spring框架会出现一大堆spring框架的介绍,以及IOC和AOP ...

  5. Android开发到底做什么?

    android开发时下很流行,且工资高需求量大.随便大街上看一堆人手里都是拿着android手机,屌丝嘛我也是用android手机的,便宜嘛,不好用了不好看了换个. android手机类等移动产品数不 ...

  6. 如何获取握手包_白话详解TCP的三次握手到底做了些什么

    作 者:雨林 来 源:cnblogs.com/yuilin 广而告之:由于此订阅号换了个皮肤,系统自动取消了读者的公众号置顶.导致用户接受文章不及时.您可以打开订阅号,选择置顶(标星)公众号,重磅干货 ...

  7. 2020Android 开发年度总结:“这一年里我到底做了些啥

    [](()前言 眼看2020年还有两天就要结束了,即将迎来2021新的一年.感觉今年比起往年要过稍稍的快一些,因为2020的开年工作时间比以往是晚了许多,基本都是在3月份左右开始投入工作.而以往基本都 ...

  8. JavaScript中的 new 操作符到底做了些什么?

    new做了什么? 使用new关键字在调用函数时,函数的内部自动创建一个新对象 将函数的作用域赋给新的对象(this会指向新的对象); 执行函数的代码(添加属性和方法) 返回新对象(实例化对象) 如果返 ...

  9. 白话详解TCP的三次握手到底做了些什么!!!

    作者:雨林 https://www.cnblogs.com/yuilin 首先简单介绍一下TCP三次握手 在TCP/IP协议中,TCP协议提供可靠的连接服务,采用三次握手建立一个连接. 第一次握手:建 ...

最新文章

  1. 散记 ~ 2016-09-23
  2. jquery-1 jquery几个小实例
  3. C# Httpclient编程
  4. 跟踪反应流–将Spring Cloud Sleuth与Boot 2结合使用
  5. 《Python Cookbook 3rd》笔记(5.9):读取二进制数据到可变缓冲区中
  6. mysql中各种连接的区别
  7. linux内核教学的全套视频,中科大Linux内核分析视频教程下载,共计23集
  8. 64位Win10 Modelsim破解及证书LICENSE.TXT无法生成解决方法
  9. js实现全国省份下拉
  10. 70行代码让你远离颈椎病,致所有的程序猿们
  11. Python计算机视觉(中英文版本)pdf+源代码
  12. 2020最新开发及环境搭建类经典面试题
  13. 微信引流推广:美拍视频简单的引流方法分享
  14. 计算机组装实验老毛桃u盘报告,老毛桃WINPE优盘(U盘)安装系统图解
  15. 一文讲解Linux 内核网络协议栈-数据从接收到ip层
  16. 英语八年级上册计算机的事实,人教版英语|八年级上册各单元必考知识点汇总,收藏!...
  17. Oracle查看表触发器
  18. Appium+Eclipse+夜神模拟器 实现QQ打电话功能
  19. 有一篇文章,共有 3 行文字,每行有 80 个字符。编写程序分别统计出其中英文大写 字母、英文小写字母、数字、空格以及其他字符的个数
  20. Gym - 102460L Largest Quadrilateral(几何-凸包+旋转卡壳求最大的四边形面积)

热门文章

  1. uniapp 自定义弹框
  2. 第3章 Groovy初学者
  3. 【Halcon】算子介绍
  4. Java开发进阶:Java编程的关键技术点有哪些?
  5. Currency Exchange poj1860
  6. 慌的一批!妹子一个rm -rf把公司服务器数据删没了...
  7. LSTM outputs和state outputs[:, -1, :] outputs[ -1]
  8. 如何运用CRM维护客户关系?
  9. CDMA弱信号掉话问题分析
  10. 配置目标跟踪开源项目traj_gen_vis踩过的坑