75 lines
5.7 KiB
Markdown
75 lines
5.7 KiB
Markdown
# Classloader
|
||
Java ClassLoader是构成JRE(Java Runtime Environment)的一部分,ClassLoader负责动态的将java classes加载到JVM中。java classes并不是一次性全部加载到内存中的,而是在应用需要时动态加载到内存中。
|
||
|
||
> JRE调用classloader,然后classloader负责将classes动态加载到内存中。
|
||
|
||
在java`按需将需要的类动态加载到内存中`的特性中,classloader扮演了重要的作用,令Java应用更加的弹性和高效。
|
||
|
||
## Types of ClassLoaders in Java
|
||
java ClassLoaders可以被归类为不同的类型,每种类型负责从指定的路径加载classes:
|
||
### Bootstrap ClassLoader(原始类加载器)
|
||
- Bootstrap Classloader是机器码,负责初始化JVM的操作
|
||
- 直到java 8中,其从`rt.jar`中加载核心java文件;但是从java 9开始,其负责从java runtime image中加载核心java文件
|
||
- Bootstrap ClassLoader独立的操作,其并没有parent ClassLoader
|
||
|
||
### Platform ClassLoader(Extension ClassLoader)
|
||
- 在java 9之前,存在Extension ClassLoader,但是在java 9及之后,其被称作Platform ClassLoader
|
||
- 该classloader会从JDK module系统中加载平台特定的拓展
|
||
- platform class loader会从java runtime image中加载文件,同时也会从`java.platform`属性所指定的module或`--module-path`所指定的module处加载文件
|
||
|
||
### System ClassLoader(Application ClassLoader)
|
||
- System ClassLoader也被称为`Application ClassLoader`,其从应用的classpath加载classes
|
||
- 其是Platform ClassLoader的一个child
|
||
- 将会从`CLASSPATH`环境变量、`-classpath`或`-cp`选项所指定的目录处加载
|
||
|
||
## Principles of Functionality of a Java ClassLoader
|
||
java ClasLoader基于如下准则进行操作:
|
||
### Delegation Model
|
||
- ClassLoaders遵循委托继承算法
|
||
- 当JVM遇到一个class时,其会检查其是否已经被加载,如果该class还未被加载,那么JVM会将class的加载委托给`a chain of ClassLoaders`
|
||
- 该委托架构从Application ClassLoader开始,向上移动到Platform ClassLoader,最终移动到Bootstrap ClassLoader
|
||
- 在层次结构中的每一个ClassLoader,都会在其负责的locations处查找class,并且在必要时将class的加载过程委托给父级
|
||
|
||
### Visibility Principle
|
||
- 由Parent ClassLoaders负责加载的classes对于child classloader来说也是可见的;反之,由child classloader负责加载的classes对于parent classloader来说则不可见
|
||
- 上述的可见性定义确保了封装性,防止由不同的classloaders负责加载的classes出现冲突
|
||
|
||
### Uniqueness Property
|
||
- ClassLoader会确保class只会被加载一次,从而保证class在JVM中的唯一性
|
||
- 只有当parent ClassLoader无法查询到某个类时,当前classloader才会尝试去加载该类
|
||
|
||
## Methods of java.lang.ClassLoader
|
||
为了加载类,ClassLoader中提供了如下方法:
|
||
- `loadClass(String name, boolean resolve)`: 其会加载JVM所引用的类,并在必要时进行解析
|
||
- `defineClass()`: 该方法会将一个字节数组转化为Class实例对象。如果字节数组中的内容不是有效的class,那么将会抛出ClassFormatError异常
|
||
- `findClass(String name)`:该方法会对class进行查找,但是并不会对其进行加载
|
||
- `findLoadedClass(String name)`:该方法用于验证是否该类已经被加载
|
||
- `Class.forName(String name, boolean initialize, ClassLoader loader)`: 该方法会加载并初始化class,允许指定ClassLoader,如果该classLoader没有指定,则默认会使用Bootstrap ClassLoader
|
||
|
||
上述方法的使用示例如下:
|
||
```java
|
||
// Code executed before class loading
|
||
Class<?> clazz = ClassLoader.getSystemClassLoader().loadClass("com.example.MyClass");
|
||
```
|
||
<img alt="ClassLoader in Java" height="inherit" src="https://media.geeksforgeeks.org/wp-content/uploads/jvmclassloader.jpg" width="inherit">
|
||
|
||
### Delegation Model
|
||
JVM和ClassLoader使用了委托层次结构算法来加载class,classLoader运行原则如下:
|
||
- classLoader永远会遵循委托层次结构原则
|
||
- 当jvm遇到class时,其首先会检查该class是否已经被加载
|
||
- 如果该类未被加载,那么jvm将会请求classloader sub-system去加载该类,而classloader sub-system则会将该加载工作委托给Application ClassLoader
|
||
- Application Classloader会将该类的加载任务委托给Extension ClassLoader,而Extension ClassLoader则是会再次将该加载任务委托给Bootstrap Classloader
|
||
- Bootstrap Classloader会在bootstrap classpath(JDK/JRE/LIB/EXT)路径下查找该类,如果该class存在,那么其将会被加载,否则加载请求将会回到Extension Classloader
|
||
- Extension Classloader也会从Extension classpath下查找该类,如果查找到该类,则加载该类,否则请求将会被委托给Application ClassLoader
|
||
- Application ClassLoader将会在Application classpath路径下查找该类,如果类存在会加载该类,否则其会抛出ClassNotFoundException
|
||
|
||
### Visibility Principle
|
||
可见性原则保证了`由parent classloader加载的类对于child classloader来说是可见的,而child classloader加载的类对parent classloader来说是不可见的`。
|
||
|
||
假设类`GEEK.class`被Extension ClassLoader加载,那么该类仅被Extension ClassLoader和Application Classloader可见,对于Bootstrap ClassLoader来说`GEEK.class`是不可见的。如果该类尝试通过Bootstrap ClassLoader再次加载,其会报`ClassNotFoundException`
|
||
|
||
### Uniqueness Property
|
||
唯一性保证了class是唯一的,其保证了由parent classloader加载的类不会再被child classloader加载。只有当parent classloader无法查找到该类时,当前classloader才会尝试对其进行加载
|
||
|
||
|