Android运行环境和类加载机制
一、Android虚拟机技术演变
Android2.2 —JIT 引入
缺陷
- 每次启动都需要编译
- 运行时耗电
Android4.4 —ART和AOT
4.4引入ART和AOT,与dalvik共存
Android5.0 — ART全面取代Dalvik
AOT静态编译,应用安装时,启动dex2oat把dex文件预编译成ELF文件
Android7.0 —JIT回归
- 应用安装时dex不会被编译
- 应用运行时,dex先通过解析器被直接执行,热点函数会被识别并被JIT编译后存储在jit code cache中并生成profile文件记录热点函数信息
- 手机进入IDLE 或 charging时,扫描profile文件,执行AOT进行编译
二、概念简介
JVM java虚拟机
软件实现 ,像物理机器一般执行程序,基于栈实现。运行java bytecode,可以在不同的机器上运行java程序。
Dalvik 虚拟机
基于寄存器的虚拟机,专门为Android平台开发。基于dex bytecode。Dalvik在每次应用运行的时候,字节码转化成机器码(just in time ,JIT)
ART 虚拟机
Android Runtime 即android运行时,Android 4.4 引入ART,会在应用第一次安装时(Ahead-of-Time,AOT),启动dex2oat预编译生成ELF,生成真正的本地应用从而提高运行效率。
JIT (just-in-time) 即时编译器
将运行频繁的 dex/odex代码编译优化,翻译成指令代码,便于底层直接执行,提升运行效率。
缺陷
- 每次运行都执行,耗电
AOT( Ahead-of-time) 运行前编译器
通过dex2oat将dex文件预编译成ELF文件,每次运行程序都无需重新编译
缺陷
- 应用安装和系统升级优化比较耗时
- 优化后会占用额外空间
.dex文件
Dalvik可执行文件格式
(javac) (dx工具)
.java ———> .class ————->.dex
.odex文件
Dalvik环境下 dexopt 对dex文件优化后存放才dalvik-cache目录下
.oat文件
dex2oat工具对dex生成oat文件
OTA (Over-The-Air Technology 无线更新)
通过无线技术,远程控制更新软件的方法
Dalvik and ART 结构示意图
三、类加载器
类的生命周期
类从被加载到虚拟机内存,到卸载为止,它的整个生命周期包括:
- 加载
- 验证(连接)
- 准备(连接)
- 解析(连接)
- 初始化
- 使用
- 卸载
其中验证、准备、解析统称为连接
类加载器
根据一个类的全限定名称,读取此类的二进制字节流到虚拟机中,然后转换成为一个与目标类对应的java.lang.Class对象实例。
每个类加载器,都拥有独立的类名空间,与类本身确立类在虚拟机中的唯一性。即:比较两个类是否相等,需要类加载器相同,并且来源自同一个class文件
虚拟机提供了3中类加载器
-
Bootstrap类加载器
负责加载虚拟机自身需要的类(如:
\lib 下的类) -
Extension类加载器
由sum.misc.Launcher$ExtClassLoader实现。负责加载java.ext.dirs 系统变量指定类库
-
应用程序类加载器
由sun.misc.Launcher$App-ClassLoader实现。负责加载用户路径指定类库
双亲委派模型
特点:
- 除了顶层类加载器,其余加载器必须有自己的父类加载器
- 父子关系以组合模式复用父类加载器代码
过程:
- 类加载器收到类加载请求,递归委派父类加载器完成
- 父类加载器无法加载,子类加载
核心加载方法loadClass
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
// 缓存查询
Class<?> c = findLoadedClass(name);
if (c == null) {
// parent加载器查询
try {
if (parent != null) {
c = parent.loadClass(name, false);
} else {
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
// 父类加载器未查询到没有查询到
}
if (c == null) {
// 缓存和父加载器都未查询到,执行查询
c = findClass(name);
}
}
return c;
}
Android类加载器
也是通过ClassLoader将目标类加载到内存。类加载器之间符合双亲委派模型。通过dex读取类
主要类加载器
- BootClassLoader: 加载Android Framework中的class文件
- PathClassLoader:默认加载器,加载已安装到系统中的Apk文件(/data/data/appdir)
- DexClassLoader:加载指定目录的class字节码文件
- BaseDexClassLoader:PathClassLoader和DexClassLoader基类
类图
在Android中
public class PathClassLoader extends BaseDexClassLoader {
public PathClassLoader(String dexPath, ClassLoader parent) {
super(dexPath, null, null, parent);
}
public PathClassLoader(String dexPath, String librarySearchPath, ClassLoader parent) {
super(dexPath, null, librarySearchPath, parent);
}
@libcore.api.CorePlatformApi
public PathClassLoader(
String dexPath, String librarySearchPath, ClassLoader parent,
ClassLoader[] sharedLibraryLoaders) {
super(dexPath, librarySearchPath, parent, sharedLibraryLoaders);
}
}
public class DexClassLoader extends BaseDexClassLoader {
public DexClassLoader(String dexPath, String optimizedDirectory,
String librarySearchPath, ClassLoader parent) {
super(dexPath, null, librarySearchPath, parent);
}
}
查看源码可知DexClassLoader 在API level26以后废弃了,和PathClassLoader 其实没区别了
无论是PathClassLoader还是DexClassLoader最终都是通过BaseDexClassLoader来实现类加载的。
源码片段
public class BaseDexClassLoader extends ClassLoader {
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
// First, check whether the class is present in our shared libraries.
if (sharedLibraryLoaders != null) {
for (ClassLoader loader : sharedLibraryLoaders) {
try {
return loader.loadClass(name);
} catch (ClassNotFoundException ignored) {
}
}
}
// Check whether the class in question is present in the dexPath that
// this classloader operates on.
List<Throwable> suppressedExceptions = new ArrayList<Throwable>();
Class c = pathList.findClass(name, suppressedExceptions);
if (c == null) {
ClassNotFoundException cnfe = new ClassNotFoundException(
"Didn't find class \"" + name + "\" on path: " + pathList);
for (Throwable t : suppressedExceptions) {
cnfe.addSuppressed(t);
}
throw cnfe;
}
return c;
}
}
BaseDexClassLoader最终是通过DexPathList进行类加载的。通过解析dex文件路径,得到dex文件对象,转化成含有DexFile的Element对象列表。最终在element列表中遍历查询的。具体如何加载可以参考插件化知识梳理(8) 类的动态加载源码分析这篇文章,不具体展开。
Android中系统类和自定义类的加载器一览
测试代码
public class App extends Application {
@Override
public void onCreate() {
super.onCreate();
ClassLoader loader = getClassLoader();
log("Application:" + loader.getClass().getName());
log("String:" + String.class.getClassLoader().getClass().getName());
log("CustomClass:" + CustomClass.class.getClassLoader().getClass().getName());
registerActivityLifecycleCallbacks(new InnerActivityLifecycleCallbacks() {
@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
log("InnerActivityLifecycleCallbacks:"+getClassLoader().getClass().getName());
log("activity:" + activity.getClass().getClassLoader().getClass().getName());
}
});
}
}
输出结果
App: Application:dalvik.system.PathClassLoader
App: String:java.lang.BootClassLoader
App: CustomClass:dalvik.system.PathClassLoader
App: RecycleView:dalvik.system.PathClassLoader
App: InnerActivityLifecycleCallbacks:dalvik.system.PathClassLoader
App: activity:dalvik.system.PathClassLoader
环境:手机Android 10 , compileSdkVersion 28
结论
-
Android SDK 都是通过 PathClassLoader加载的
-
jdk 相关类是通过 BootClassLoader加载的
- app用户相关类 通过 PathClassLoader 加载
- 三方库 通过PathClassLoader 加载
四、应用
补丁热修复
插件化
参考:
https://www.jianshu.com/p/59d98244fb52
https://juejin.im/post/5c232907f265da61662482b4
https://source.android.com/devices/tech/dalvik
https://en.wikipedia.org/wiki/Just-in-time_compilation
https://juejin.im/post/5c31c3ebf265da614e2c3b68