一文入门类加载器

388 阅读3分钟

java.lang.class类

Class类的实例表示正在运行的Java应用程序中的类或接口. Class类没有公共构造函数,那他是如何创建的呢? 我们来看看官方文档是如何说的

Instead {@code Class} objects are constructed automatically by the Java Virtual Machine as classes are loaded and by calls to the {@code defineClass} method in the class loader.

Class对象是由Java虚拟机在加载类时自动构造的,并通过调用类加载器中的defineClass方法来构造. 每个Class对象包含一个定义他的类加载器的引用.

private final ClassLoader classLoader;

下面我们就来认识一下类加载器.


什么是类加载器

看看jdk文档怎么说:

A class loader is an object that is responsible for loading classes.Given the binary name of a class, a class loader should attempt to locate or generate data that constitutes a definition for the class

类加载器是负责加载类的对象,给定一个类的全限定名,类加载应该尝试生成包含在类定义里面的数据流 通俗点解释就是: .java文件编译后生成.class文件, 类加载器就是将.class文件转换成java.lang.Class类的一个实例


类加载器的分类

启动类加载器(BootstrapClassLoader)

负责加载系统类,由c语言实现,没有对应的类加载器 加载路径: System.getProperty("sun.boot.class.path");

扩展类加载器(Extension Class Loader)

负责加载扩展类 加载路径 System.getProperty("java.ext.dirs");

应用类加载器(AppClassLoader)

从用户类路径下加载 加载路径 System.getProperty("java.class.path");

验证

System.out.println(String.class.getClassLoader()); // 加载系统类用到的类加载器
System.out.println(Student.class.getClassLoader()); // Student是用户自定义的类

输出一下查看结果

null
sun.misc.Launcher$AppClassLoader@18b4aac2

因为String是系统类,所以由BootstrapClassLoader加载,而BootstrapClassLoader没有对应的类加载器,所以为null

Student是在用户类路径下定义的,所以是由AppClassLoader加载


双亲委派机制

除了启动类加载器外,所有的类加载器都有一个父类加载器。类加载器要先把加载类的机会交给他的父类加载器在父类加载器加载失败时,他才会加载该类

类加载器层次结构图:

类加载2.png

加载Studeng类的过程: AppClassLoader先交给其父类ExtentsionClassLoader加载,ExtensionClassLoader交给他的父类BootstrapClassLoader加载,BootstrapClassLoader没有父类加载器,尝试加载,加载失败,让其子类ExtensionClassLoader加载,同样加载失败,交给其子类AppClassLoader加载,加载成功!

源码追踪

sun.misc.Launcher类

Launcher类是java应用的入口,由JVM创建的,源码对外隐藏,只能通过反编译查看. 为什么Launcher类是java应用的入口? 因为 ApplicationClassLoader和ExtClassLoader都是在Launcher类定义的

双亲委派机制源码

ClassLoader类的loadClass()方法

protected Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException
    {
        // 加锁机制
        synchronized (getClassLoadingLock(name)) {
            // 给定一个类的全限定名,检查类是否被加载过,底层调用的c++方法,不需要深入研究
            Class<?> c = findLoadedClass(name);
            if (c == null) {
                long t0 = System.nanoTime();
                try {
                    // 先让其父类加载
                    if (parent != null) {
                        c = parent.loadClass(name, false);
                    } else {
                        // 父类为空,则让BootStrapClassLoader加载该类
                        c = findBootstrapClassOrNull(name);
                    }
                } catch (ClassNotFoundException e) {
                    
                }

                if (c == null) {
                   
                    long t1 = System.nanoTime();
                     // 通过类的全限定名查找该类
                    c = findClass(name);

                    // this is the defining class loader; record the stats
                    sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                    sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                    sun.misc.PerfCounter.getFindClasses().increment();
                }
            }
            if (resolve) {
                resolveClass(c);
            }
            return c;
        }
    }

大家自己都debug几遍,就能搞懂双亲委派机制的流程,我就不贴图了,求关注


关注公众号,免费领取优秀jvm入门书籍