ClassLoader源码解析

作者: 奔跑吧李博 | 来源:发表于2020-09-12 00:01 被阅读0次

    Java中的所有类,必须被装载到jvm中才能运行,这个装载工作是由jvm中的类装载器完成的,类装载器所做的工作实质是把类文件从硬盘读取到内存中,JVM在加载类的时候,都是通过ClassLoader的loadClass()方法来加载class的,loadClass使用双亲委派模式。

    官方给出ClassLoader功能解释:
    类加载器是负责加载类的对象。ClassLoader类是一个抽象类。给定类的二进制名称,类加载器应尝试查找或生成构成该类定义的数据。一种典型的策略是将名称转换为文件名,然后从文件系统中读取该名称的“类文件”。

    ClassLoader分类

    Android中的ClassLoader主要分为BootClassLoader、PathClassLoader和DexClassLoader这三种类型。BootClassLoader:主要负责加载Android FrameWork层中的字节码文件; PathClassLoader:负责加载已经安装到系统APK文件中的字节码文件;DexClassLoader:负责加载指定目录中的字节码文件

    BootClassLoader部分代码:

    class BootClassLoader extends ClassLoader {
    
        private static BootClassLoader instance;
    
        @FindBugsSuppressWarnings("DP_CREATE_CLASSLOADER_INSIDE_DO_PRIVILEGED")
        public static synchronized BootClassLoader getInstance() {
            if (instance == null) {
                instance = new BootClassLoader();
            }
    
            return instance;
        }
    
        public BootClassLoader() {
            super(null);
        }
        ...
    }
    

    BootClassLoader 继承自ClassLoader抽象类,实现方式为单例模式,需要注意的是BootClassLoader的访问修饰符是默认的,只有在同一个包中才可以访问,所以我们在应用程序中是无法直接调用到的。

    PathClassLoader类代码:

    public class PathClassLoader extends BaseDexClassLoader {
        public PathClassLoader(String dexPath, ClassLoader parent) {
            super((String)null, (File)null, (String)null, (ClassLoader)null);
            throw new RuntimeException("Stub!");
        }
    
        public PathClassLoader(String dexPath, String librarySearchPath, ClassLoader parent) {
            super((String)null, (File)null, (String)null, (ClassLoader)null);
            throw new RuntimeException("Stub!");
        }
    }
    

    PathClassLoader继承自BaseDexClassLoader,PathClassLoader构造方法中各个参数的含义:

    dexPath:dex文件以及包含dex的apk文件或jar文件的路径集合,多个路径用文件分隔符分隔,默认文件分隔符为‘:’。
    librarySearchPath:所使用到的C/C++库存放的路径
    parent:该ClassLoader所对应的父ClassLoader

    DexClassLoader类代码:

    public class DexClassLoader extends BaseDexClassLoader {
        public DexClassLoader(String dexPath, String optimizedDirectory, String librarySearchPath, ClassLoader parent) {
            super((String)null, (File)null, (String)null, (ClassLoader)null);
            throw new RuntimeException("Stub!");
        }
    }
    

    DexClassLoader 也是继承自BaseDexClassLoader ,相比较PathClassLoader而言,DexClassLoader的构造方法中多了一个参数optimizedDirectory。

    optimizedDirectory:Android系统将dex文件进行优化后所生成的ODEX文件的存放路径,该路径必须是一个内部存储路径。PathClassLoader中使用默认路径“/data/dalvik-cache”,而DexClassLoader则需要我们指定ODEX优化文件的存放路径。

    上述三种ClassLoader中,PathClassLoader的parent为BootClassLoader,DexClassLoader的parent同样为BootClassLoader。

    loadClass方法代码:

        protected Class<?> loadClass(String name, boolean resolve)
            throws ClassNotFoundException
        {
                // First, check if the class has already been loaded
                Class<?> c = findLoadedClass(name);
                if (c == null) {
                    try {
                        if (parent != null) {
                            c = parent.loadClass(name, false);
                        } else {
                            c = findBootstrapClassOrNull(name);
                        }
                    } catch (ClassNotFoundException e) {
                        // ClassNotFoundException thrown if class not found
                        // from the non-null parent class loader
                    }
    
                    if (c == null) {
                        // If still not found, then invoke findClass in order
                        // to find the class.
                        c = findClass(name);
                    }
                }
                return c;
        }
    
        protected final Class<?> findLoadedClass(String name) {
            ClassLoader loader;
            if (this == BootClassLoader.getInstance())
                loader = null;
            else
                loader = this;
            return VMClassLoader.findLoadedClass(loader, name);
        }
    

    调用findLoadedClass(String)方法检查这个类是否被Jvm加载过;如果父加载器为Null,类加载器装载虚拟机内置的加载器调用findClass(String)方法装载类, 如果,按照以上的步骤成功的找到对应的类,并且该方法接收的resolve参数的值为true,那么就调用resolveClass(Class)方法来处理类。 ClassLoader的子类最好覆盖findClass(String)而不是这个方法。 除非被重写,这个方法默认在整个装载过程中都是同步的(线程安全的)。

    类加载的动态性体现:
    一个应用程序总是由n多个类组成,Java程序启动时,并不是一次把所有的类全部加载后再运行,它总是先把保证程序运行的基础类一次性加载到jvm中,其它类等到jvm用到的时候再加载,这样的好处是节省了内存的开销,因为java最早就是为嵌入式系统而设计的,内存宝贵,这是一种可以理解的机制,而用到时再加载这也是java动态性的一种体现。

    类加载器之间是如何协调工作的:
    java中有三个类加载器,问题就来了,碰到一个类需要加载时,它们之间是如何协调工作的,即java是如何区分一个类该由哪个类加载器来完成呢。 在这里java采用了委托模型机制,这个机制简单来讲,就是“类装载器有载入类的需求时,会先请示其Parent使用其搜索路径帮忙载入,如果Parent 找不到,那么才由自己依照自己的搜索路径搜索类”。

    类的加载过程:
    1. 装载:通过“类全路径名”查找并加载类的二进制数据
    2. 链接:把类的二进制数据合并到JRE中

    验证:确保被加载类的正确性,确保加载内容不危害虚拟机;
    准备:为类的静态变量分配内存,并将其初始化为默认值;
    解析:把类中的符号引用转换为直接引用;

    1. 初始化
      静态代码块执行初始化操作

    参考
    深度分析Java的ClassLoader机制
    Android ClassLoader源码解析
    ClassLoader源码解析

    相关文章

      网友评论

        本文标题:ClassLoader源码解析

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