JVM之类加载器详解
上图是整个JVM内存结构解析图
本文主要针对类的加载器进行讲解
类加载器ClassLoader
负责加载class文件,class文件在文件开头有特定的文件标示,并且ClassLoader只负责class文件的加载,至于它是否可以运行,则由Execution Engine决定。
类的加载器分为四种:
- BootstrapClassLoader:启动类加载器,主要加载jre/lib/rt.jar(核心类库),C/C++编写
- ExtensionClassLoader:扩展类加载器,负责加载rt以外的jre的jar包
- AppclassLoader:应用程序类加载器,加载第三方jar包,当前应用的classpath的所有类
- 自定义类加载器:用户自定义的加载器,属于Java.lang.ClassLoader的子类(一般用不上)
类加载器的加载过程
JVM中有这几种类加载器,那么它们的加载过程是怎么样的呢?三个类加载器的加载范围都是可以修改的,如果三个类加载器的加载范围重叠了,重叠区的类是由哪个类加载器加载呢?即使区域不重叠,如果一个类在多个目录都出现了,这些目录被不同的类加载器读取,该类由哪个加载器加载呢?
- 当AppClassLoader加载一个class时,它首先判断这个class在自己的缓存中是否已经存在(之前是否已经加载过),如果存在就直接返回,如果不存在它不会自己进行加载,而是把类加载请求委派给父类加载器ExtClassLoader去完成。
- 当ExtClassLoader加载一个class时,它首先也会判断这个class在自己的缓存中是否已经存在,如果存在就直接返回,如果不存在则把类加载请求委派给BootStrapClassLoader去完成。
- 如果BootStrapClassLoader缓存中存在,直接返回,如果不存在,尝试从自己的加载目录中加载,如果成功加载,直接返回,如果加载失败(在$JAVA_HOME/jre/lib/rt.jar里未查找到该class),则会将加载请求返回给ExtClassLoader来尝试加载。
- 若ExtClassLoader也加载失败,则会将加载请求返回给AppClassLoader来加载
- 如果AppClassLoader也加载失败,则会报出异常ClassNotFoundException
其实这就是所谓的双亲委派模型。简单来说:如果一个类加载器收到了类加载的请求,它不会自己去加载这个类,而是把请求委托给父加载器去完成,最终委托给顶层加载器BootsrtapClassLader去加载,如果加载失败,则返回给子加载器去完成,返回到AppClassLader还加载失败的话则抛出异常。
那么为什么要采用双亲委派原则呢?这样的好处是什么?
我们都知道java的核心类库中有4000多个类库,很多我们都不知道的类库,如果在开发中,我们无意中定义了一个类是存在java核心类库中的类的话,如果不是双亲委派原则,核心类库就会被覆盖,那么就会破坏JDK的稳定性。如果一个类可以被多个类加载器扫描到,最终是由最顶层的父加载器加载,防止内存中出现多份同样的字节码(安全性角度)。
在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方法中:
-
执行findLoadedClass(String)去检测这个class是不是已经加载过了。
-
执行父加载器的loadClass方法。如果父加载器为null,则jvm内置的加载器去替代,也就是Bootstrap ClassLoader。这也解释了ExtClassLoader的parent为null,但仍然说Bootstrap ClassLoader是它的父加载器。
-
如果向上委托父加载器没有加载成功,则通过findClass(String)查找。
本文地址:https://blog.csdn.net/weixin_46238462/article/details/110879399