这么多年, Android 虚拟机到底做了些什么?
在 Android 操作系统中,有一个非常重要的核心部分: Android Runtime。说到这个,我相信很多人都听到过 Dalvik、ART、JIT 以及 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 编译出热点代码的机器码。更好地提高使用效率。
- 生成 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 过的设备。
- 接入 ProfileInstaller
dependencies {implementation("androidx.profileinstaller:profileinstaller:1.2.0-beta01")
}
PS: com.android.tools.build:gradle 需要使用 7.1.0-alpha01 及以上的版本,官方推荐使用 7.3.0-beta01 以上的版本。
- 将第一步生成的 proflie 文件重命名为
baseline-prof.txt
放入到src/main
目录下,与 AndroidManifest.xml 文件统计。
简单几步就能使用 Baseline Profile能力了。让应用程序运行性能更好。
以上为 Android 操作系统中虚拟机的进化过程,以及开发者可以在加速 APP 运行可以做的事情。
这么多年, Android 虚拟机到底做了些什么?相关推荐
- python中mod是什么意思_【python中,mod_python到底做了些什么呢?】mod python 教程
python 编程小白 ,不会用doctest 请大神指教怎么用!! >>> >>> def is_between(v, lower, higher): ... ...
- 软件在安装时,到底做了些什么?
软件在安装时,到底做了些什么? 大家每天都在用电脑,可能也经常在自己的电脑上安装软件.就算自己没安装过,至少也看到人家安装过软件.在这里,我不是想教你怎么安装软件,而是想向你展示,软件在安装的过程中, ...
- 一个菜鸟的暑期到底做了些什么
一个菜鸟的暑期都做了些什么 linux虚拟机的搭建 PhotoShop的学习 暑期主攻:Web前端 总结 linux虚拟机的搭建 先从最简单的开始说起,依据网上的流程以及学长的帮助,搭建好了虚拟机,但 ...
- 什么是spring框架?spring框架到底有什么用?spring框架到底做了些什么?
什么是spring框架,spring框架究竟有什么用呢?我们可以用spring框架来做些什么呢?这是我今天要说的内容. 当然,百度spring框架会出现一大堆spring框架的介绍,以及IOC和AOP ...
- Android开发到底做什么?
android开发时下很流行,且工资高需求量大.随便大街上看一堆人手里都是拿着android手机,屌丝嘛我也是用android手机的,便宜嘛,不好用了不好看了换个. android手机类等移动产品数不 ...
- 如何获取握手包_白话详解TCP的三次握手到底做了些什么
作 者:雨林 来 源:cnblogs.com/yuilin 广而告之:由于此订阅号换了个皮肤,系统自动取消了读者的公众号置顶.导致用户接受文章不及时.您可以打开订阅号,选择置顶(标星)公众号,重磅干货 ...
- 2020Android 开发年度总结:“这一年里我到底做了些啥
[](()前言 眼看2020年还有两天就要结束了,即将迎来2021新的一年.感觉今年比起往年要过稍稍的快一些,因为2020的开年工作时间比以往是晚了许多,基本都是在3月份左右开始投入工作.而以往基本都 ...
- JavaScript中的 new 操作符到底做了些什么?
new做了什么? 使用new关键字在调用函数时,函数的内部自动创建一个新对象 将函数的作用域赋给新的对象(this会指向新的对象); 执行函数的代码(添加属性和方法) 返回新对象(实例化对象) 如果返 ...
- 白话详解TCP的三次握手到底做了些什么!!!
作者:雨林 https://www.cnblogs.com/yuilin 首先简单介绍一下TCP三次握手 在TCP/IP协议中,TCP协议提供可靠的连接服务,采用三次握手建立一个连接. 第一次握手:建 ...
最新文章
- 散记 ~ 2016-09-23
- jquery-1 jquery几个小实例
- C# Httpclient编程
- 跟踪反应流–将Spring Cloud Sleuth与Boot 2结合使用
- 《Python Cookbook 3rd》笔记(5.9):读取二进制数据到可变缓冲区中
- mysql中各种连接的区别
- linux内核教学的全套视频,中科大Linux内核分析视频教程下载,共计23集
- 64位Win10 Modelsim破解及证书LICENSE.TXT无法生成解决方法
- js实现全国省份下拉
- 70行代码让你远离颈椎病,致所有的程序猿们
- Python计算机视觉(中英文版本)pdf+源代码
- 2020最新开发及环境搭建类经典面试题
- 微信引流推广:美拍视频简单的引流方法分享
- 计算机组装实验老毛桃u盘报告,老毛桃WINPE优盘(U盘)安装系统图解
- 一文讲解Linux 内核网络协议栈-数据从接收到ip层
- 英语八年级上册计算机的事实,人教版英语|八年级上册各单元必考知识点汇总,收藏!...
- Oracle查看表触发器
- Appium+Eclipse+夜神模拟器 实现QQ打电话功能
- 有一篇文章,共有 3 行文字,每行有 80 个字符。编写程序分别统计出其中英文大写 字母、英文小写字母、数字、空格以及其他字符的个数
- Gym - 102460L Largest Quadrilateral(几何-凸包+旋转卡壳求最大的四边形面积)
热门文章
- uniapp 自定义弹框
- 第3章 Groovy初学者
- 【Halcon】算子介绍
- Java开发进阶:Java编程的关键技术点有哪些?
- Currency Exchange poj1860
- 慌的一批!妹子一个rm -rf把公司服务器数据删没了...
- LSTM outputs和state outputs[:, -1, :] outputs[ -1]
- 如何运用CRM维护客户关系?
- CDMA弱信号掉话问题分析
- 配置目标跟踪开源项目traj_gen_vis踩过的坑