Android虚拟机
Android应用程序运行在Dalvik/ART虚拟机,并且每一个应用程序对应一个单独的虚拟机实例。Dalvik虚拟机也算一个Java虚拟机,只是它执行的不是class文件,而是dex文件。Dalvik虚拟机的指令集是基于寄存器的。
Dalvik与ART
Dalvik虚拟机执行的是dex字节码,解释执行。从Android2.2版本开始,支持JIT即时编译在程序运行的过程中进行热点代码编译。Dalvik使用JIT来将字节码转换成机器码,效率低。
ART是Android5.0以及更高版本默认虚拟机。ART虚拟机执行的是本地机器码,但并没有要求开发者将自己的应用编译成机器码,那这个机器码怎么来的?
ART采用了AOT预编译技术,在安装时使用设备自带的dex2oat工具编译应用,dex中的字节码被编译成本地机器码。所以ART会占用更多的应用安装时间和存储空间。
总结来说就是,Dalvik虚拟机是在程序运行的时候才将字节码转为机器码,而ART虚拟机在程序安装的时候就把字节码转为机器码了。所以Android5.0开始安装的时候比较慢,就是这个原因。
但是!
从Android N开始,ART使用AOT编译、解释和JIT即时编译。安装应用的时候不在进行AOT编译了,所以节省了安装时间,运行过程中解释执行,对经常执行的方法进行JIT,经过JIT编译的方法信息将会记录到Profile配置文件中。当设备闲置和充电时,编译守护进程会运行,根据Profile配置文对常用代码进行AOT编译,待下次运行时直接使用。
Android中的ClassLoader
ClassLoader种类
- BootClassLoader 主要加载Android中FrameWork的一些class文件
- PathClassLoader 加载已经安装到系统中的apk的一些class文件
- DexClassLoader 加载指定目录的class文件
- BaseDexClassLoader
我们比较关注的就是PathClassLoader和DexClassLoader,众所周知,ActivityThread类中的main()方法是整个应用的入口,在应用启动的时候,系统就创建了PathClassLoader。
源码分析
我们看一下PathClassLoader的源码:
1 | public class PathClassLoader extends BaseDexClassLoader { |
PathClassLoader的父类是BaseDexClassLoader,而且他俩都没有重写loadClass方法,所以看一下BaseDexClassLoader的父类ClassLoader中
1 | public Class<?> loadClass(String name) throws ClassNotFoundException { |
检查这个类的class文件是否加载过,如果加载过直接返回该class文件,如果没有则判断parent是否加载过,如果parent也没有加载过,则调用findClass查找。而findClass是由ClassLoader各个子类自己重写的。
那么parent是谁呢?是通过构造方法传进来的ClassLoader,系统在创建PathClassLoader的时候传进来的是BootClassLoader,而不是指的父类BaseDexClassLoader!
也就是说先在BootClassLoader里面找是否已经加载过了,没有的话我在找自己的。这是双亲委托机制。好处有两方面:
- 避免重复加载
- 安全性考虑,防止核心API被随意篡改
PathClassLoader是没有重写findClass方法的,下面是BaseDexClassLoader源码,重写了父类ClassLoader的findClass方法。BaseDexClassLoader 中维护了一个 DexPathList, DexPathList 中又维护这一个 Element 数组,这个数组中 Element元素 其实就是 Dex 文件。
1 | @Override |