类加载器

作者: 秀儿2020 | 来源:发表于2020-05-19 15:39 被阅读0次

类加载机制的基本过程

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中的

相关文章

网友评论

    本文标题:类加载器

    本文链接:https://www.haomeiwen.com/subject/ffdbohtx.html