JVM(五)类加载

2022-04-20  本文已影响0人  NIIIICO

一、类文件

1、结构:

ClassFile {
    u4 magic;// 魔数,用于确定文件类型
    u2 minor_version;// 副版本号
    u2 major_version;// 主版本号,代表JDK版本,如:0034,十进制为52,表示JDK8
    u2 constant_pool_count;// 常量池数量
    cp_info constant_pool[ constant_pool_count - 1];// 常量池
    u2 access_flags;// 标记,识别类是class还是接口;是否是public;是否是abstract;是否是final等
    u2 this_class;// 当前类
    u2 super_class;// 父类
    u2 interfaces_count;// 接口数量
    u2 interfaces[ interfaces_count];// 接口
    u2 fields_count;// 字段数量
    field_info fields[ fields_count];// 字段
    u2 methods_count;// 方法数量
    method_info methods[ methods_count];// 方法
    u2 attributes_count;// 属性数量
    attribute_info attributes[ attributes_count];// 属性
}

文件通过二进制存储,以8个字节为一组,下图为16进制视图下类文件的内容:

class文件内容

具体内容及常量池结构等,可以参考Java Language and Virtual Machine Specifications

2、生命周期

类从被加载到虚拟机内存到从内存卸载,包含以下几个阶段:


类的生命周期
(1)加载
类信息在jvm中的存储方式
(2)验证
验证
(3)准备
(4)解析
(5)初始化
(6)使用
(7)卸载
引用关系

二、类加载器

负责读取指定目录下的class字节码文件,并进行解析,然后转换成方法区的响应结构。

1、分类

类加载器分为两类:启动(引导)类加载器(Bootstrap ClassLoader)、自定义类加载器(User-Defined ClassLoader)。Java虚拟机规范将所有派生于抽象类ClassLoader的类加载器都划分为自定义类加载器。

注意:父加载器和继承没有关系,是类加载器中的parent字段。

(1)启动(引导)类加载器 BootstrapClassLoader
(2)自定义类加载器 User-DefinedClassLoader

<2.1>扩展类加载器 ExtClassLoader

<2.2>应用程序类加载器 AppClassLoader

<2.3>自定义类加载器

2、双亲委派

双亲委派

3、android的类加载器

(1)BootClassLoader
(2)BaseDexClassLoader
(3)DexClassLoader
(4)PathClassLoader
(5)类加载流程

由于DexClassLoader、PathClassLoader继承自BaseDexClassLoader,而BaseDexClassLoader继承自ClassLoader,当我们调用loadClass时,会调用到如下代码:

public abstract class ClassLoader {
    static private class SystemClassLoader {
        public static ClassLoader loader = ClassLoader.createSystemClassLoader();
    }

    private static ClassLoader createSystemClassLoader() {
        String classPath = System.getProperty("java.class.path", ".");
        String librarySearchPath = System.getProperty("java.library.path", "");
        // 创建PathClassLoader
        return new PathClassLoader(classPath, librarySearchPath, BootClassLoader.getInstance());
    }

    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 {
                    // 然后,如果有父加载器,调用父加载器的loadClass
                    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.
                    // 最后,如果还未找到,调用findClass加载
                    c = findClass(name);
                }
            }
            return c;
    }
}

上述代码可以看到,创建PathClassLoader传入的parent是BootClassLoader 。

class BootClassLoader extends ClassLoader {
    protected Class<?> loadClass(String className, boolean resolve)
           throws ClassNotFoundException {
        Class<?> clazz = findLoadedClass(className);

        if (clazz == null) {
            clazz = findClass(className);
        }

        return clazz;
    }

    protected Class<?> findClass(String name) throws ClassNotFoundException {
        return Class.classForName(name, false, null);
    }
}

如果BootClassLoader没加载到class,则会回到PathClassLoader的代码中执行,执行到它的findClass方法。在findClass中,会通过DexPathList查找class。如果最终未找到,会报ClassNotFoundException异常。

public class BaseDexClassLoader extends ClassLoader {
    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        List<Throwable> suppressedExceptions = new ArrayList<Throwable>();
        // 通过pathList寻找class
        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;
    }
}


final class DexPathList {
    private Element[] dexElements;

    public Class<?> findClass(String name, List<Throwable> suppressed) {
        // 遍历element,寻找class
        for (Element element : dexElements) {
            Class<?> clazz = element.findClass(name, definingContext, suppressed);
            if (clazz != null) {
                return clazz;
            }
        }

        if (dexElementsSuppressedExceptions != null) {
            suppressed.addAll(Arrays.asList(dexElementsSuppressedExceptions));
        }
        return null;
    }
}

4、热修复原理

上文已经知道Android的类加载流程,可以看到会通过BaseDexClassLoader.pathList来寻找class,而DexPathList又是通过字段dexElements进行查找,热修复就是通过反射拿到DexPathList,生成新的Element数据,将他的dexElements替换掉从而达成修复的目的。

上一篇 下一篇

猜你喜欢

热点阅读