美文网首页
ClassLoader笔记

ClassLoader笔记

作者: 水言 | 来源:发表于2018-01-31 15:50 被阅读32次

    Java ClassLoader简介

    java 开发中编写的都是.java的对象文件,当我们需要运行当前的项目的时候,当前项目会被编译,编译后需要用到的.java文件就会被被编译成.class文件。但是Java程序 .class文件并不是本地可执行的,当运行Java程序时,首先运行JVM(Java虚拟机),然后再把Java class加载到JVM里头运行,负责加载Java class的这部分就叫做ClassLoader

    JVM本身包含了一个ClassLoader称为Bootstrap ClassLoader,和JVM一样,Bootstra pClassLoader是用C++语言实现的。另外JVM还会提供了ExtClassLoader和AppClassLoader,它们都是用Java语言编写的,由BootstrapClassLoader加载。

    JDK提供的三个ClassLoader,根据层级从高到低为:

    • BootstrapClassLoader ,主要加载JVM自身工作需要的类。
    • ExtClassLoaderr,主要加载%JAVA_HOME%\lib\ext目录下的库类。
    • AppClassLoader,主要加载Classpath指定的库类,一般情况下这是程序中的默认类加载器,也是ClassLoader.getSystemClassLoader() 的返回值。(这里的Classpath默认指的是环境变量中配置的Classpath,但是可以在执行Java命令的时候使用-cp 参数来修改当前程序使用的Classpath)
    ClassLoader的继承关系

    Android ClassLoader简介

    android应用程序,本质上使用的是java开发,使用标准的java编译器编译出Class文件,和普通的java开发不同的地方是把class文件再重新打包成dex类型的文件。Android虚拟机运行的dex字节码,一种对class文件优化的产物,传统Class文件是一个Java源码文件会生成一个.class文件,而Android是把所有Class文件进行合并,优化,然后生成一个最终的class.dex。
    加载这样特殊的.dex文件就需要特殊的类装载器,所以android中提供了DexClassLoader类。

    Android系统中类加载器有:

    • BootClassLoader,Android系统启动时会使用BootClassLoader来预加载常用类,它是由java实现的。
    • PathClassLoader,Android系统使用PathClassLoader来加载系统类和应用程序的类
    • DexClassLoader,可以加载dex文件以及包含dex的apk文件或jar文件
    Android ClassLoad继承关系

    PathClassLoader和DexClassLoader继承自BaseDexClassLoader,并且它们具体方法实现都在BaseDexClassLoader中。
    Android系统使用PathClassLoader来加载系统类和应用程序的类,如果是加载非系统应用程序类,则会加载data/app/目录下的dex文件以及包含dex的apk文件或jar文件。PathClassLoader不建议开发直接使用。
    libcore/dalvik/src/main/java/dalvik/system/PathClassLoader.java

    public class PathClassLoader extends BaseDexClassLoader {
    //*   dexPath:dex文件以及包含dex的apk文件或jar文件的路径集合,多个路径用文件分隔符分隔,默认文件分隔符为‘:’。
    //*   librarySearchPath:包含 C/C++ 库的路径集合,多个路径用文件分隔符分隔分割,可以为null。
    //*   parent:ClassLoader的parent。
        public PathClassLoader(String dexPath, ClassLoader parent) {
            super(dexPath, null, null, parent);
        }
        public PathClassLoader(String dexPath, String librarySearchPath, ClassLoader parent) {
            super(dexPath, null, librarySearchPath, parent);
        }
    }
    

    DexClassLoader可以加载dex文件以及包含dex的apk文件或jar文件,也支持从SD卡进行加载,这也就意味着DexClassLoader可以在应用未安装的情况下加载dex相关文件。因此,它是热修复和插件化技术的基础。来查看它的代码,如下所示。
    libcore/dalvik/src/main/java/dalvik/system/DexClassLoader.java

    public class DexClassLoader extends BaseDexClassLoader {
    //多一个optimizedDirectory参数
        public DexClassLoader(String dexPath, String optimizedDirectory,
                String librarySearchPath, ClassLoader parent) {
            super(dexPath, new File(optimizedDirectory), librarySearchPath, parent);
            }
    }
    

    DexClassLoader构造方法的参数要比PathClassLoader多一个optimizedDirectory参数,参数optimizedDirectory代表什么呢?我们知道应用程序第一次被加载的时候,为了提高以后的启动速度和执行效率,Android系统会对dex相关文件做一定程度的优化,并生成一个ODEX文件,此后再运行这个应用程序的时候,只要加载优化过的ODEX文件就行了,省去了每次都要优化的时间,而参数optimizedDirectory就是代表存储ODEX文件的路径,这个路径必须是一个内部存储路径。
    PathClassLoader没有参数optimizedDirectory,这是因为PathClassLoader已经默认了参数optimizedDirectory的路径为:/data/dalvik-cache

    ClassLoader双亲委托机制

    JVM以及Dalvik均是通过ClassLoader来加载类的,加载类的实现方式我们称为 双亲委托模型。

    如果一个类加载器收到了类加载的请求,他首先不会自己去尝试加载这个类,而是把这个请求委托给自己的父加载器,每一层的类加载器都是如此,因此所有的类加载请求最终都应该传送到顶层的Bootstrap ClassLoader中,只有当父加载器反馈自己无法完成加载请求时,子加载器才会尝试自己加载。


    java中ClassLoader加载顺序

    双亲委托模型的重要用途是为了解决类载入过程中的安全性问题。

    假设有一个开发者自己编写了一个名为java.lang.Object的类,想借此欺骗JVM。现在他要使用自定义ClassLoader来加载自己编写的java.lang.Object类。然而幸运的是,双亲委托模型不会让他成功。因为JVM会优先在BootstrapClassLoader的路径下找到java.lang.Object类,并载入它。

    双亲委托的代码实现java.lang.ClassLoader#loadClass,系统自带的其他的子类都继承了此方法且没有进行复写。

    protected Class<?> loadClass(String className, boolean resolve) throws ClassNotFoundException {
    //判断该类有没有加载各国
            Class<?> clazz = findLoadedClass(className);
            if (clazz == null) {
                ClassNotFoundException suppressed = null;
    //先尝试由父ClassLoader去加载
                try {
                    clazz = parent.loadClass(className, false);
                } catch (ClassNotFoundException e) {
                    suppressed = e;
                }
    //如果父ClassLoader没找到该类,调用自己的findClass方法。
                if (clazz == null) {
                    try {
                        clazz = findClass(className);
                    } catch (ClassNotFoundException e) {
                        e.addSuppressed(suppressed);
                        throw e;
                    }
                }
            }
            return clazz;
        }
    

    参考:
    http://blog.csdn.net/markzy/article/details/53192993
    https://www.cnblogs.com/linux007/p/5782677.html
    http://blog.csdn.net/Mr_LiaBill/article/details/50497055
    http://blog.csdn.net/itachi85/article/details/78088701

    相关文章

      网友评论

          本文标题:ClassLoader笔记

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