ClassLoader加载机制(源码浅析)

类加载机制

加载是指查找字节流,并且据此创建类的过程。加载需要借助类加载器,在 Java 虚拟机中,类加载器使用了双亲委派模型,即接收到加载请求时,会先将请求转发给父类加载器。

ClassLoader双亲委派机制

在这里插入图片描述
类加载器加载类步骤:

  1. 首先会自底向上检测是否加载过此Class(即在缓存区中是否有此Class),如果有直接返回Class对象;
  2. 判断父加载器是否存在,如果存在父加载器,则请求父类加载器来载入此Class,如果成功则返回Class对象;如果不成功则使用子类加载器来载入此Class,如果成功则返回Class对象,如果不成功则抛出ClassNotFoundException()异常;
  3. 如果不存在父类加载器(要么父类加载器是根加载器;要么本身就是根加载器),请求根类加载器是来载入此Class,如果成功则返回Class对象,如果不成功则抛出ClassNotFoundException()异常;

源码浅析(关键方法)

loadClass(String name, boolean resolve)
name是类的名字,resolve是解析

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
404行会进入一个同步锁,因为有可能涉及到多个线程调用一个ClassLoader加载同一个类,保证线程安全,避免冲突;

406行Class<?> c = findLoadedClass(name);会寻找曾经是否加载过目标Class,如果有会直接跳过407的if和432的if,直接return c;,即返回目标Class对象;

如果没有加载过目标Class,会直接进入407行的 if,408行会进入计时(可以忽略);则会判断parent是否为空,如果parent不为null,那么当前ClassLoader的父加载器,再次进入loadClass方法即401行;

如果当父加载器是BootStrapClassLoader时或者自己本身就是BootStrapClassLoader,由于BootStrapClassLoader是C++编写的,parent则会返回null

进入412行的elsec = findBootstrapClassOrNull(name);去查找BootStrapClassLoader有没有载入过目标Class;若载入过,返回Class对象;否则会进入BootStrapClassLoader目录下扫描是有目标Class,如果有返回,没有就会返回;

进入420行的ifc = findClass(name);会在当前ClassLoader目录中去查找是否有目标Class;如果有就装载目标Class并返回,如果没有就会轮到子类ClassLoader来查找,最终会到自定义ClassLoader去查找是否有目标Class,如果有返回,没有就会抛出ClassNotFoundException()异常;

ClassLoader之间的关系

package ClassLoader;

public class ClassLoaderDemo {

    public static void main(String[] args) {
        Class c = ClassLoaderDemo.class;

        //由于是使用系统类加载器(非自定义加载器)来加载ClassLoaderDemo类,应该输出AppClassLoader
        System.out.println(c.getClassLoader());
        //AppClassLoader的父加载器应该是ExtClassLoader
        System.out.println(c.getClassLoader().getParent());
        //ExtClassLoader的父加载器应该是BootStrapClassLoader(BootStrapClassLoader应该输出null)
        System.out.println(c.getClassLoader().getParent().getParent());

    }
}

在这里插入图片描述

自定义ClassLoader——>AppClassLoader——>ExtClassLoader——>BootStrapClassLoader(null)

Tips:为什么BootStrapClassLoader是null?

在上面的413行代码中提到c = findBootstrapClassOrNull(name);
在这里插入图片描述
在这里插入图片描述
native会去调用本地库或者外置都非Java代码(在openjdk中可以查看)

为什么要使用双亲委派机制加载类?

避免多份同样字节码重复加载,内存是有限的,没有必要保存相同的Class对象。

例如:System.out.println();

类A打印时会加载一次System类;类B打印时又会加载一次System类,那么内存中就会存在两份相同的System字节码,造成没必要的内存消耗;

如果使用双亲委派机制加载类,加载过后就不会再重复加载相同的Class;

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章