天天看点

小白从0到1看jvm(2、类加载器与双亲委派机制)

作者:言沫东

一、概述

网上有许多介绍jvm的文章和视频,但大多枯燥、晦涩难懂,很难让小白坚持下去;如果你是一个小白,不用担心,在本文中,我们将从0到1,一步步地介绍JVM的基本概念和工作原理。无论你是想深入了解Java语言,还是准备参加Java开发的面试,这篇文章都将为你提供有用的知识和技能。让我们一起开始这段有趣的旅程吧!

小白从0到1看jvm(2、类加载器与双亲委派机制)

二、正文

类加载器

idea编译后的.class文件,通过类加载器将之加载到JVM内存中,并放在运行时数据区的方法区内,然后再创建相对应的java.lang.Class对象,用来封装类在方法区内的数据结构。

类加载器种类

类加载器名称 说明
Bootstrap ClassLoader 【启动类加载器】

1、采用c/c++实现,嵌套在jvm内部

2、加载Java核心类库(JAVA_HOME/jre/lib/rt.jar),用于提供JVM自身需要的类

3、无法直接访问

Extension ClassLoader 【扩展类加载器】

1、采用java语言编写

2、加载(JAVA_HOME/jre/lib/ext)下类库

3、父类加载器:启动类加载器

Application ClassLoader【应用程序类加载器】

1、采用java语言编写

2、加载“classpath”环境变量所指定的路径中的类,可以理解为加载我们自己写的Java代码,以及我的导入的三方Jar包中的代码

3、父类加载器:扩展类加载器

自定义类加载器 1、继承ClassLoader类,可定制类的加载方式
小白从0到1看jvm(2、类加载器与双亲委派机制)

类加载时采用了双亲委派模式,什么是双亲委派模式

当一个类加载器收到类加载的请求时,自己不会立刻去加载这个类,而是把加载请求委派给父类加载器完成,父类加载器也会做相同的处理;当父加载器在自己的搜索范围内找不到需要加载的类时,子加载器才会尝试自己去加载,最后如果任何一个加载器都找不到这个类时,就会抛出:ClassNotFoundException异常。

classloader源码分析

public Class<?> loadClass(String name) throws ClassNotFoundException {
        return loadClass(name, false);
    }

protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
        synchronized (getClassLoadingLock(name)) {
            // 检查一下指定名称的类是否已经加载过,如果加载过了,就不需要再加载,直接返回
            Class<?> c = findLoadedClass(name);
            if (c == null) {
                long t0 = System.nanoTime();
                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) {
                    long t1 = System.nanoTime();
                  	// 如果都没有找到,则通过自定义实现的findClass去查找并加载
                    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;
        }
    }           

为什么需要双亲委派模式

假如没有双亲委派机制,此时用户编写一个和jdk自带的一模一样的类(比如:java.lang.Object),并放到了Classpath中,那么应用程序类加载器就把这个当做Object加载到了内存中,从而造成不可预知的后果;而双亲委派机制先一路向上委托,启动类加载器去找的时候,就把正确的Object加载到了内存中,后面再加载用户编写的Object类时,是不会加载运行的。

所以:JDK自带类是无法被覆盖的,而引入第三方的JAR是可以自己定义相同的类来覆盖的。

继续阅读