JVM学习(二)续1-ClassLoader代码解读-双亲委派模型

1. ClassLoader的继承关系

ClassLoader是什么鬼?为什么我们要如此大费周章的讲解这个?
还记得AppClassLoader、ExtClassLoader么?他们与ClassLoader之间的关系是什么?


JVM学习(二)续1-ClassLoader代码解读-双亲委派模型_第1张图片
ClassLoader继承关系
JVM学习(二)续1-ClassLoader代码解读-双亲委派模型_第2张图片
AppClassLoader+ExtClassLoader

URLClassLoader

SecureClassLoader

ClassLoader

2. ClassLoader重要方法loadclass()代码解读。

直接上代码,代码上注释有说明。

```
public Class loadClass(String name) throws ClassNotFoundException {
    return loadClass(name, false);
}

protected Class loadClass(String name, boolean resolve) //resolve字段表示是否进行【连接】阶段处理
    throws ClassNotFoundException
{
    synchronized (getClassLoadingLock(name)) {
        // First, check if the class has already been loaded
        // 首先,判断该类是否已经加载过了。
        Class c = findLoadedClass(name);
        if (c == null) {
            long t0 = System.nanoTime();
            try {
                if (parent != null) { //如果父类存在
                    // 如果未加载过,则委派给父类进行加载。
                    c = parent.loadClass(name, false);
                } else {
                    // 如果父类不存在,则交给BootstrapClassLoader来加载。 什么时候父类不存在呢?其实就是ExtClassLoader不存在父类的情况。
                    c = findBootstrapClassOrNull(name);
                }
            } catch (ClassNotFoundException e) {
                // ClassNotFoundException thrown if class not found
                // from the non-null parent class loader
                // 如果父类通过缓存+加载都无法找到,并抛出ClassNotFoundException异常时,则捕获异常但不处理。
            }

            if (c == null) {
                // If still not found, then invoke findClass in order
                // to find the class.                   
                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;
    }
}
```

代码中有几个关键调用需要注意:

Class c = findLoadedClass(name)通过缓存查找判断是否存在该类。
  进一步查看该方法实现,又调用了native findLoadedClass0方法。
    ```
    protected final Class findLoadedClass(String name) {
        if (!checkName(name))
            return null;
        return findLoadedClass0(name);
    }

    private native final Class findLoadedClass0(String name);
    ```
② 当parent != null时,c = parent.loadClass(name, false);。如果父类不为空,则委派给父类的loadClass()方法执行。
当 parent == null是,```c = findBootstrapClassOrNull(name);```父类如果为空时,则委派给BootstrapClassLoader来查找。

这里就是双亲委派模型出现了。

③ 当在经过父类们缓存查找和加载后,仍然未找到该类,则本加载器会亲自进行查找c = findClass(name);。这个方法很关键。
    ```
    protected Class findClass(String name) throws ClassNotFoundException {
            throw new ClassNotFoundException(name);
        }
    ```

3. 双亲委派模型的验证

    public static void main(String[] args) {
        ClassLoader loader = TestStatic3.class.getClassLoader();
        System.out.println(loader);
        System.out.println(loader.getParent());
        System.out.println(loader.getParent().getParent());
    }

输出结果:

sun.misc.Launcher$AppClassLoader@b4aac2
sun.misc.Launcher$ExtClassLoader@193b845
null

4. 双亲委派模型的优点

这里补充下几个双亲委派模型的特点。

  • 系统类防止内存中出现多份同样的字节码
    因为Java类随着它的类加载器一起具备了一种带有优先级的层次关系。双亲委派模型很好的解决了各个类加载器的基础类的统一问题(越基础的类由越上层的加载器进行加载)。
  • 保证Java程序安全稳定运行
    使用双亲委派模型来组织类加载器之间的关系,有一个显而易见的好处就是Java类随着它的类加载器一起具备了一种带有优先级的层次关系。例如,类java.lang.Object,它存放在rt.jar中,无论哪一个类加载器要加载这个了类,最终都是委派给模型最顶端的启动类加载器进行加载,因此Object类在程序的各个类加载器环境中都是同一个类。
    相反,如果没有使用双亲委派模型,由各个类加载器自行去加载的话,如果用户自己编写了一个称为java.lang.Object的类,并放在程序的ClassPath中,那系统中将会出现多个不同的Object类,Java类型体系中最基本的行为也就无法保证,应用程序也会变得一片混乱。

你可能感兴趣的:(JVM学习(二)续1-ClassLoader代码解读-双亲委派模型)