美文网首页
Java基础-类加载机制(ClassLoader)

Java基础-类加载机制(ClassLoader)

作者: 涛涛123759 | 来源:发表于2021-08-12 20:34 被阅读0次

Android知识总结

一、JVM 的类加载架构

ClassLoader
  • 1、Bootstrap ClassLoader(启动类加载器)
    Bootstrp加载器是用C++语言写的,它是在Java虚拟机启动后初始化的,它主要负责加载%JAVA_HOME%/jre/lib,-Xbootclasspath参数指定的路径及%JAVA_HOME%/jre/classes中的类。
  • 2、Extension ClassLoader(扩展类加载器)
    Bootstrp loader加载ExtClassLoader,并且将ExtClassLoader的父加载器设Bootstrp loader.ExtClassLoader是用Java写的,具体来说就是sun.misc.Launcher$ExtClassLoader,ExtClassLoader主要加载%JAVA_HOME%/jre/lib/ext,此路径下的所有classes目录以及java.ext.dirs系统变量指定的路径中类库
  • 3、Application ClassLoader(系统类加载器) Bootstrp loader加载完ExtClassLoader后,就会加载AppClassLoader,并且将AppClassLoader`的父加载器指定为 ExtClassLoader。AppClassLoader也是用Java写成的,它的实现类是 sun.misc.Launcher$AppClassLoader,另外我们知道ClassLoader中有个getSystemClassLoader方法,此方法返回的正是AppclassLoader.AppClassLoader主要负责加载classpath所指定的位置的类或者是jar文档,它也是Java程序默认的类加载器。
  • 4、Custom ClassLoader(自定义类加载器java.lang.ClassLoader的子类)
    在程序运行期间, 通过java.lang.ClassLoader的子类动态加载class文件, 体现java动态实时类装入特性。

二、类加载器的特性

每个ClassLoader都维护了一份自己的名称空间, 同一个名称空间里不能出现两个同名的类。
为了实现java安全沙箱模型顶层的类加载器安全机制, java默认采用了 双亲委派的加载链 结构。

双亲委派的加载链

2.1、双亲委托模式

某个类加载器在加载类时,首先将加载任务委托给父类加载器,依次递归,如果父类加载器可以完成类加载任务,就成功返回;只有父类加载器无法完成此加载任务或者没有父类加载器时,才自己去加载。

每个类加载器都有自己的加载缓存,当一个类被加载了以后就会放入缓存,等下次加载的时候就可以直接返回了。

当前classLoader的缓存中没有找到被加载的类的时候,委托父类加载器去加载,父类加载器采用同样的策略,首先查看自己的缓存,然后委托父类的父类去加载,一直到BootClassLoader。

双亲委托机制特点:

  • 因为这样可以避免重复加载,当父亲已经加载了该类的时候,就没有必要子ClassLoader再加载一次。

  • 考虑到安全因素,我们试想一下,如果不使用这种委托模式,那我们就可以随时使用自定义的String来动态替代java核心api中定义类型,这样会存在非常大的安全隐患,而双亲委托的方式,就可以避免这种情况,因为String已经在启动时被加载,所以用户自定义类是无法加载一个自定义的ClassLoader。(沙箱安全机制)

    //parent 是父类 BootClassLoader
    private final ClassLoader parent;

    protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException{
        // 检查class是否有被加载
        Class c = findLoadedClass(name);
        if (c == null) {
            long t0 = System.nanoTime();
            try {
                if (parent != null) {
                    //如果parent不为null,则调用parent的loadClass进行加载
                    c = parent.loadClass(name, false);
                } else {
                    //parent为null,则调用BootClassLoader进行加载
                    c = findBootstrapClassOrNull(name);
                }
            } catch (ClassNotFoundException e) {
            }
            if (c == null) {
                // 如果都找不到就自己查找
                long t1 = System.nanoTime();
                c = findClass(name);
            }
        }
        return c;
    }

因此我们自己创建的ClassLoader: new PathClassLoader("/sdcard/xx.dex", getClassLoader()); 并不仅仅只能加载 xx.dex中的class。
值得注意的是: c = findBootstrapClassOrNull(name);
按照方法名理解,应该是当parent为null时候,也能够加BootClassLoader加载的类。
new PathClassLoader("/sdcard/xx.dex", null),能否加载Activity.class?
但是实际上,Android当中的实现为:(Java不同)

    private Class findBootstrapClassOrNull(String name) {
        return null;
    }

2.1、findClass

可以看到在所有父ClassLoader无法加载Class时,则会调用自己的 findClass 方法。 findClass 在ClassLoader中的定义为:

protected Class<?> findClass(String name) throws ClassNotFoundException {
    throw new ClassNotFoundException(name);
}

其实任何ClassLoader子类,都可以重写loadClass 与 findClass 。一般如果你不想使用双亲委托,则重写loadClass 修改其实现。而重写 findClass 则表示在双亲委托下,父ClassLoader都找不到Class的情况下,定义自己如何去查找一个Class。而我们的PathClassLoader 会自己负责加载 MainActivity 这样的程序中自己编写的类,利用双亲委托父ClassLoader加载Framework中的 Activity 。说PathClassLoader 并没有重写 loadClass ,因此我们可以来看看PathClassLoader中的 findClass 是如何实现的。最终在BaseDexClassLoader中调用。

    public BaseDexClassLoader(String dexPath, File optimizedDirectory,String
            librarySearchPath, ClassLoader parent) {
        super(parent);
        //创建DexPathList
        this.pathList = new DexPathList(this, dexPath, librarySearchPath,
                optimizedDirectory);
    }
    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        List<Throwable> suppressedExceptions = new ArrayList<Throwable>();
        //查找指定的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;
    }

实现非常简单,从 pathList 中查找class。继续查看 DexPathList

    public DexPathList(ClassLoader definingContext, String dexPath,
                       String librarySearchPath, File optimizedDirectory) {
        //.........
        // splitDexPath 实现为返回 List<File>.add(dexPath)
        // makeDexElements 会去 List<File>.add(dexPath) 中使用DexFile加载dex文件返回 Element数组
        this.dexElements = makeDexElements(splitDexPath(dexPath), optimizedDirectory,
                suppressedExceptions, definingContext);
        //.........
    }

    public Class findClass(String name, List<Throwable> suppressed) {
        //从element中获得代表Dex的 DexFile
        for (Element element : dexElements) {
            DexFile dex = element.dexFile;
            if (dex != null) {
                //查找class
                Class clazz = dex.loadClassBinaryName(name, definingContext, suppressed);
                if (clazz != null) {
                    return clazz;
                }
            }
        }
        if (dexElementsSuppressedExceptions != null) {
            suppressed.addAll(Arrays.asList(dexElementsSuppressedExceptions));
        }
        return null;
    }

三、Android 中 CloassLoader示意图

任何一个 Java 程序都是由一个或多个 class 文件组成,在程序运行时,需要将 class 文件加载到 JVM 中才可以使用,负责加载这些 class 文件的就是 Java 的类加载机制。ClassLoader 的作用简单来说就是加载 class 文件,提供给程序运行时使用。每个 Class 对象的内部都有一个 classLoader 字段来标识自己是由哪个 ClassLoader 加载的。

class Class<T> {
   ... 
  private transient ClassLoader classLoader;
   ...
 }
CloassLoader

从图中可以看出ClassLoader是一个抽象类,而它的具体实现类主要有:

  • ClassLoader
    ClassLoader是一个抽象类,其中定义了ClassLoader的主要功能;
  • BootClassLoader
    BootClassLoader是ClassLoader的内部类,用于预加载preload()常用类以及一些系统Framework层级需要的类;
  • BaseDexClassLoader
    BaseDexClassLoader继承ClassLoader,是抽象类ClassLoader的具体实现类,PathClassLoader和DexClassLoader都继承它
  • PathClassLoader
    PathClassLoader用于Android应用程序类加载器。如果是加载非系统应用程序类,则会加载data/app/目录下的dex文件以及jar、zip、apk中的classes.dex。
  • DexClassLoader
    DexClassLoader可以加载自定义的dex文件以及jar、zip、apk中的classes.dex,,也支持从SD卡进行加载。

四、类加载示意图

类加载示意图

小福利

在线源码阅读:
ANDROID社区

AndroidXRef

相关文章

  • java类加载机制学习笔记

    java类加载机制 类加载器(classloader) ClassLoader 类 Bootstrap Class...

  • ClassLoader深入学习记录

    ClassLoader是什么 ClassLoader是Java的类加载机制ClassLoader用于动态加载cla...

  • JAVA类加载机制

    jvm之java类加载机制和类加载器(ClassLoader)的详解java类加载机制:全盘负责、双亲委派、缓存机...

  • 为什么说SPI打破双亲委派机制

    简单介绍ClassLoader的双亲委派机制: java类通过Classloader加载,Classloader之...

  • 类加载器的概述

    类加载器是JVM执行类加载机制的前提 ClassLoader的作用: ClassLoader是Java的核心组件,...

  • Android的类加载器

    Android的ClassLoader与java的ClassLoader有些差异, 双亲委托机制某个类加载器在加载...

  • JVM

    Java 类的加载机制 类加载器 启动类加载器:Bootstrap ClassLoader,负责加载存放在JDK\...

  • 高逼格JVM技术该如何学?

    Java 类的加载机制 类加载器 启动类加载器:Bootstrap ClassLoader,负责加载存放在JDK\...

  • ClassLoader

    类加载机制[1] Java 默认提供的三个 ClassLoader BootStrap ClassLoader 称...

  • Java自定义类加载器

    一、ClassLoader作用 Java程序在运行的时候,JVM通过类加载机制(ClassLoader)把clas...

网友评论

      本文标题:Java基础-类加载机制(ClassLoader)

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