admin

浅谈 JVM 类加载器与类加载机制
概述类从被加载到虚拟机内存开始,到卸载出内存位置,它的生命周期分为以下 7 个阶段:加载->验证->准...
扫描右侧二维码阅读全文
15
2018/03

浅谈 JVM 类加载器与类加载机制

概述

类从被加载到虚拟机内存开始,到卸载出内存位置,它的生命周期分为以下 7 个阶段:
加载->验证->准备->解析->初始化->使用->卸载
整个过程涉及到的东西非常多,目前个人理解还比较有限,就不做过多分析,本文主要就类的加载器及加载模型做个简单介绍。

类加载器介绍

在 Java 虚拟机中,可以将类加载器细分为以下 4 种:

  • 启动类加载器(Bootstrap ClasssLoader)
  • 扩展类加载器(Extension ClassLoader)
  • 应用程序类加载器(Application ClassLoader)
  • 用户自定义类加载器(User ClassLoader)

下面简单说下它们各自的功能。

启动类加载器(Bootstrap ClasssLoader)

这个类加载器主要负责将放在$JAVA_HOME/lib中的,或者被-Xbootclasspath参数所指定的路径中的,并且是虚拟机识别的(仅按照文件名识别,如 rt.jar,名字不符合的类库即使放在 lib 目录中也不会被加载)类库加载到虚拟机内存中。

扩展类加载器(Extension ClassLoader)

该类加载器它负责将$JAVA_HOME/lib/ext中的,或者被java.ext.dirs系统环境变量所指定的路径中的所有类加载到虚拟机内存中,相比启动类加载器,我们可以将一些第三方的类库放到$JAVA_HOME/lib/ext目录中,扩展类也是会加载的,这种暴力的使用场景也很常见,有时候在我们的项目中类冲突了或者其他原因导致找不到类,这个时候就将该类的 jar 包直接丢进$JAVA_HOME/lib/ext目录,问题即可暂时解决。

应用程序类加载器(Application ClassLoader)

这个类加载器是ClassLoader中的getSystemClassLoader方法的返回值,所以一般也称作系统类加载器,它负责加载用户类路径(ClassPath)中所包含的类库。

用户自定义类加载器(User ClassLoader)

顾名思义,这种类加载器是由程序员通过继承ClassLoader自己实现的。

类加载模型

以上 4 种类加载器通常情况下都不是单独存在的,而是一起配合来完成加载工作的,比如按以下的关系进行配合加载:
无标题.png
加载关系是这样的:除了启动类加载器外,其他类型的类加载器加载类的时候,都不会直接自己去加载,而是去让它的父节点去加载,如果父节点加载失败,最后再由自己来加载。
上面这种类加载器之间的这种层次关系,称为类加载器的双亲委派模型(Parents Delegation Model)
我们可以通过ClassLoaderloadClass方法看到上面那种加载关系:

protected Class<?> loadClass(String name, boolean 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 {
                        c = findBootstrapClassOrNull(name);
                    }
                } catch (ClassNotFoundException e) {
                    // ClassNotFoundException thrown if class not found
                    // from the non-null parent class loader
                }

                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;
        }
    }

只要除了启动类加载器以外所有的类加载器都遵循上面的加载机制,并且之间设置好父子关系,那么就构成了双亲委派模型,一般来说,用户自定义类加载器,继承ClassLoader之后,不需要重写上面的loadClass方法,只需要重写findClass方法即可,这样就确保写出来的类加载器是符合双亲委派模型的。

总结

革命尚未成功,同志还需继续努力。

Last modification:March 15th, 2018 at 09:00 pm
If you think my article is useful to you, please feel free to appreciate

Leave a Comment