欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

JVM之类加载器详解

程序员文章站 2022-01-06 07:44:43
上图是整个JVM内存结构解析图本文主要针对类的加载器进行讲解类加载器ClassLoader负责加载class文件,class文件在文件开头有特定的文件标示,并且ClassLoader只负责class文件的加载,至于它是否可以运行,则由Execution Engine决定。类的加载器分为四种:BootstrapClassLoader:启动类加载器,主要加载jre/lib/rt.jar(核心类库),C/C++编写ExtensionClassLoader:扩展类加载器,负责加载rt以外的jre的j....

JVM之类加载器详解
上图是整个JVM内存结构解析图

本文主要针对类的加载器进行讲解

类加载器ClassLoader

负责加载class文件,class文件在文件开头有特定的文件标示,并且ClassLoader只负责class文件的加载,至于它是否可以运行,则由Execution Engine决定。
类的加载器分为四种:

  1. BootstrapClassLoader:启动类加载器,主要加载jre/lib/rt.jar(核心类库),C/C++编写
  2. ExtensionClassLoader:扩展类加载器,负责加载rt以外的jre的jar包
  3. AppclassLoader:应用程序类加载器,加载第三方jar包,当前应用的classpath的所有类
  4. 自定义类加载器:用户自定义的加载器,属于Java.lang.ClassLoader的子类(一般用不上)

类加载器的加载过程

JVM中有这几种类加载器,那么它们的加载过程是怎么样的呢?三个类加载器的加载范围都是可以修改的,如果三个类加载器的加载范围重叠了,重叠区的类是由哪个类加载器加载呢?即使区域不重叠,如果一个类在多个目录都出现了,这些目录被不同的类加载器读取,该类由哪个加载器加载呢?

  1. 当AppClassLoader加载一个class时,它首先判断这个class在自己的缓存中是否已经存在(之前是否已经加载过),如果存在就直接返回,如果不存在它不会自己进行加载,而是把类加载请求委派给父类加载器ExtClassLoader去完成。
  2. 当ExtClassLoader加载一个class时,它首先也会判断这个class在自己的缓存中是否已经存在,如果存在就直接返回,如果不存在则把类加载请求委派给BootStrapClassLoader去完成。
  3. 如果BootStrapClassLoader缓存中存在,直接返回,如果不存在,尝试从自己的加载目录中加载,如果成功加载,直接返回,如果加载失败(在$JAVA_HOME/jre/lib/rt.jar里未查找到该class),则会将加载请求返回给ExtClassLoader来尝试加载。
  4. 若ExtClassLoader也加载失败,则会将加载请求返回给AppClassLoader来加载
  5. 如果AppClassLoader也加载失败,则会报出异常ClassNotFoundException

其实这就是所谓的双亲委派模型。简单来说:如果一个类加载器收到了类加载的请求,它不会自己去加载这个类,而是把请求委托给父加载器去完成,最终委托给顶层加载器BootsrtapClassLader去加载,如果加载失败,则返回给子加载器去完成,返回到AppClassLader还加载失败的话则抛出异常。

那么为什么要采用双亲委派原则呢?这样的好处是什么?
我们都知道java的核心类库中有4000多个类库,很多我们都不知道的类库,如果在开发中,我们无意中定义了一个类是存在java核心类库中的类的话,如果不是双亲委派原则,核心类库就会被覆盖,那么就会破坏JDK的稳定性。如果一个类可以被多个类加载器扫描到,最终是由最顶层的父加载器加载,防止内存中出现多份同样的字节码(安全性角度)。
JVM之类加载器详解
在ClassLoader类中:

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) {
                        //父加载器不为空则调用父加载器的loadClass
                        c = parent.loadClass(name, false);
                    } else {
                        //父加载器为空则调用Bootstrap Classloader
                        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();
                    //父加载器没有找到,则调用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()
                resolveClass(c);
            }
            return c;
        }
    }

在loaderClass方法中:

  1. 执行findLoadedClass(String)去检测这个class是不是已经加载过了。

  2. 执行父加载器的loadClass方法。如果父加载器为null,则jvm内置的加载器去替代,也就是Bootstrap ClassLoader。这也解释了ExtClassLoader的parent为null,但仍然说Bootstrap ClassLoader是它的父加载器。

  3. 如果向上委托父加载器没有加载成功,则通过findClass(String)查找。

本文地址:https://blog.csdn.net/weixin_46238462/article/details/110879399

相关标签: java jvm