Java 类加载器
类加载器负责读取 Java 字节代码,并转换成 java.lang.Class 类的一个实例。
1 | /** |
- 可以通过
Class.getClassLoader()
获取类加载器; - 基本数据类型及其对应的数组类型没有类加载器;
- 非基本数据类型和其对应的数组的类型加载器相同;
Class 类
1 | public final class Class<T> implements java.io.Serializable, GenericDeclaration, Type, AnnotatedElement { |
类加载器分类
启动类加载器
BootstrapClassLoader 负责加载 JVM 运行时核心类,这些类位于 JAVA_HOME/jre/lib/rt.jar
文件中
可以通过 java -verbose:class HelloWorld
命令查看加载的类。
扩展类加载器
ExtClassLoader 它由 sun.misc.Launcher$ExtClassLoader 实现。它负责加载 JAVA_HOME/jre/lib/ext
目录下或者由系统变量 -Djava.ext.dir
指定位路径中的类库,开发者可以直接使用标准扩展类加载器。
应用类加载器
AppClassLoader 也叫 System ClassLoader。它由 sun.misc.Launcher$AppClassLoader 实现。一般情况下是程序的默认的类加载器,通过 ClassLoader#getSystemClassLoader() 方法可以获取到该类加载器。
URLClassLoader
用于加载通过网络传输的类或资源。子类有 ExtClassLoader 和 AppClassLoader。
1 | /** |
ClassLoader 源码分析
1 | public abstract class ClassLoader { |
类加载器常用方法如下:
- loadClass(String name, boolean resolve)
This method is used to load the classes which are referenced by the JVM. It takes the name of the class as a parameter. This is of type loadClass(String, boolean). - defineClass()
The defineClass() method is a final method and cannot be overriden. This method is used to define a array of bytes as an instance of class. If the class is invalid then it throws ClassFormatError. - findClass(String name)
This method is used to find a specified class. This method only finds but doesn’t load the class. - findLoadedClass(String name)
This method is used to verify whether the Class referenced by the JVM was previously loaded or not. - Class.forName(String name, boolean initialize, ClassLoader loader)
This method is used to load the class as well as initialize the class. This method also gives the option to choose any one of the ClassLoaders. If the ClassLoader parameter is NULL then Bootstrap ClassLoader is used.
加载类的一般步骤:
- ClassLoader always follows the Delegation Hierarchy Principle.
- Whenever JVM comes across a class, it checks whether that class is already loaded or not.
- If the Class is already loaded in the method area then the JVM proceeds with execution.
- If the class is not present in the method area then the JVM asks the Java ClassLoader Sub-System to load that particular class, then ClassLoader sub-system hands over the control to Application ClassLoader.
- Application ClassLoader then delegates the request to Extension ClassLoader and the Extension ClassLoader in turn delegates the request to Bootstrap ClassLoader.
- Bootstrap ClassLoader will search in the Bootstrap classpath(JDK/JRE/LIB). If the class is available then it is loaded, if not the request is delegated to Extension ClassLoader.
- Extension ClassLoader searches for the class in the Extension Classpath(JDK/JRE/LIB/EXT). If the class is available then it is loaded, if not the request is delegated to the Application ClassLoader.
- Application ClassLoader searches for the class in the Application Classpath. If the class is available then it is loaded, if not then a ClassNotFoundException exception is generated.
双亲委派模型
当一个类加载器接收到类加载请求时,首先会请求其父类加载器加载,当父类加载器无法找到这个类时子类加载器才会去加载。
JVM 在加载一个 class 时会先调用 classloader 的 loadClassInternal 方法,该方法源码如下:
1 | //ClassLoader.java |
Java 虚拟机是如何判定两个 Java 类是相同的?
- 类全名相同;
- 被同一个 ClassLoader 实例加载;
ClassNotFoundException
是 loadClass() 在调用 findClass() 方法中抛出的,目标类在每个类加载器的扫描路径中都不存在时,会抛出该异常。
NoClassDefFoundError
是 ClassLoader.defineClass() 方法抛出的,通过 checkName()
方法检查类的全路径是否合法。
1 | // ClassLoader.java |
参考
[1] ClassLoader - oracle docs
[2] 深入探讨 Java 类加载器 - IMB
[3] Understanding Java class loading - oracle blogs
[4] sun/misc/Launcher.java