类加载机制的基本过程
Java运行时,会根据类的完全限定名去寻找并加载类,负责加载类的类就是类加载器,它的输入是完全限定的类名,输出是Class对象。
三种类加载器
类加载器共有三个,分别是启动类加载器(Bootstrap ClassLoader)、扩展类加载器(Extension ClassLoader)和应用程序类加载器(Application ClassL),其中扩展类加载器在Java 9以后被平台类加载器(Platform Class Loader)替换。
public class ClassLoaderDemo {
public static void main(String[] args){
ClassLoader classLoader = ClassLoaderDemo.class.getClassLoader();
while (classLoader != null){
System.out.println(classLoader.getClass().getSimpleName());
classLoader = classLoader.getParent();
}
}
}
输出为:
AppClassLoader
PlatformClassLoader
- BootClassLoader
用于加载Android Framework层class文件 - PathClassLoader
用于Android应用程序类加载器,可以加载指定的dev以及jar、zip、apk中的dex文件 - DexClassLoader
加载指定的dex,以及jar、zip、apk中的dex文件
双亲委派模型
这三种类加载器之间是父子委托关系,注意不是父子继承关系。子ClassLoader有一个变量parent指向了父ClassLoader,在子ClassLoader加载一个类时,会先通过其父ClassLoader加载,如果加载成功,直接返回Class对象,在未加载成功时,子ClassLoader自己尝试加载类。这种类加载的过程被称为”双亲委派“模型
ClassLoader加载类与Class通过forName加载类的区别
通过ClassLoader的loadClass加载类后,不会执行类的初始化代码,而通过Class的forName方法加载类后,会执行类的初始化代码。
public class ClassLoaderInitDemo {
public static class Demo{
static {
System.out.println("this is init code.");
}
}
public static void main(String[] args){
ClassLoader classLoader = ClassLoader.getSystemClassLoader();
String name = ClassLoaderInitDemo.class.getName() + "$Demo";
try {
//使用类加载器加载类,类加载后,不会执行类的初始化代码
// Class<?> cls = classLoader.loadClass(name);
//使用Class的forName加载类后,会执行类的初始化代码
Class<?> cls = Class.forName(name);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
当使用ClassLoader加载静态内部类后,static中的代码不会执行,但是使用Class的forName加载类后,会输出”this is init code.“
this is init code.
自定义ClassLoader
通过继承ClassLoader并且重写findClass就可以实现自定义的ClassLoader。通过自定义的ClassLoader,一是可以实现隔离,而是可以实现热部署。
隔离
一个复杂的程序,内部可能按模块组织,不同模块可能使用同一个类,如果使用同一个类加载器,该类只会有一个Class对象,如果模块间应用该类的版本不同,可能无法共存,这就需要借助自定义ClassLoader,加载多次,生成多个Class对象,从而实现模块间的隔离,Tomcat使用他隔离不同的Web对象。
热部署
使用同一个ClassLoader,类只会被加载一次,加载后,即使class文件已经改变,再次加载,得到的还是原来的Class对象,而使用MyClassLoader,则可以先创建出一个新的ClassLoader,再用它加载Class,得到的Class对象就是最新的,从而实现动态更新。
补充
Dalvik
Dalvik是Google公司自己设计的用于Android平台的Java虚拟机,与JVM的区别在于,Dalvik运行的是dex文件,而JVM运行的是class文件,dex文件是专为Dalvik设计的一种的压缩格式,可以看为多个class文件的整合体,适合内存和处理器速度有限的系统。JVM的指令集是基于栈的,而Dalvik的指令集是基于寄存器的,运行效率更高。
ART
Android Runtime 是Android 4.4中引入的一个开发者选项,在5.0以后,变为默认模式。机器能运行的是机器码,但是Java编译生成的是字节码,机器不能直接运行, 所以Java的运行效率要比C、C++运行效率慢。但是ART是在应用安装的时候预编译字节码到机器语言,这一机制叫(Ahead-Of-Time)预编译,以提高运行应用的运行效率,但是安装速度会变慢。
- 在Dalvik下,应用需要将字节码解释成机器码后才可以执行,常用热点代码通过即时编译器(Just In Time,JIT)将字节码转换为机器码,运行效率低,而在ART环境中,应用在安装时,就会将字节码转换成机器码(AOT),安装慢,但是运行效率高。
- ART占用空间比Dalvik大,因为将字节码变味了机器码,所以这事一种空间换时间的方式。
- 预编译也可以明显改善电池续航,因为应用程序在运行时不需要重复编译了,从而减少了CPU的使用频率,降低了能耗。
ART会兼容Dalvik,在应用安装时,经过AOT,将dex文件转换为oat文件,之后ART直接运行oat文件(一种机器语言),但是ART也可以加载dex文件解释执行,此处运行流程同Dalvik。
注:.dex文件经过AOT过程变为.oat文件,然后在ART中运行。
Dexopt和DexAot
- dexopt 是对dex文件进行验证和优化,变为odex(Optimized dex)文件,目的是提高dex文件的运行效率,是针对Dalvik虚拟机的
- dexAot 是在安装时对dex文件执行dexopt之后再将odex文件进行AOT预编译操作,编译为oat可执行的文件(机器码),是针对ART中的
网友评论