JVM 系列 ClassLoader
Posted binarylei
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JVM 系列 ClassLoader相关的知识,希望对你有一定的参考价值。
JVM 系列()ClassLoader
在前面一节中,主要介绍了 Class 的装载过程,Class 的装载大体上可以分为加载类、连接类和初始化 3 个阶段。本小节将主要介绍绍 Java 语言中的 ClassLoader,类装载器。它主要工作在 Class 装载的加载阶段从系统外部获得 Class 二进制数据流。
一、ClassLoader
ClassLoader 是 Java 的核心组件,所有的 Class 都是由 ClassLoader 进行加载的, ClassLoader 负责通过各种方式将 Class 信息的二进制数据流读入系统,然然后交给 Java 虚拟机进行连接、初始化等操作。因此, Classloader在整个装载阶段,只能影响到类的加载,而无法通过 ClassLoader 去改变类的连接和初始化行为。
从代码层面看, ClassLoader 是一个抽象类,它提供了一些重要的接口,用于自定义 Class 的加载流程和加载方式。 Classloader 的主要方法如下
private final ClassLoader parent;
/**
* 给定一个类名,加载一个类,返回代表这个类的 Class 实例,如果找不到类,则返回 ClassNotFoundException 异常
*/
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
}
/**
* 将二进制字节码流解析为 Class 实例
*/
protected final Class<?> defineClass(String name, byte[] b, int off, int len)
throws ClassFormatError {
return defineClass(name, b, off, len, null);
}
/**
* 查找一个类,这是一个受保护的方法,也是重载 ClassLoader 时,重要的系统扩展点。
* 这个方法会在 loadClass() 时被调用,用于自定义查找类的逻辑。
*/
protected Class<?> findClass(String name) throws ClassNotFoundException {
throw new ClassNotFoundException(name);
}
/**
* 寻找已经加载的类
*/
protected final Class<?> findLoadedClass(String name) {
if (!checkName(name))
return null;
return findLoadedClass0(name);
}
在 ClassLoader 的结构中,还有一个重要的字段 parent,它也是一个 ClassLoader 的实例,这个字段所表示的 ClassLoader 也称为这个 ClassLoader 的双亲。在类加载的过程中, ClassLoader 可能会将某些请求交予自己的双亲处理。
二、ClassLoader 分类
在标准的 Java 程序中,Java 虚拟机会创建 3 类 ClassLoader 为整个应用程序服务。 它们分别是: BootstrapClassLoader(启动类加载器)、 ExtensionClassLoader(扩展类加载器)和 AppClassLoader(应用类加载器,也称为系统类加载器)。 此外,每一个应用程序还可以拥有自定义的 ClassLoader,扩展 Java 虚拟机获取 Class 数据的能力。
- BootstrapClassLoader 加载 rt.jar
- ExtensionClassLoader 加载 $JAVA_HOME/lib/ext/*.jar
- AppClassLoader 加载 classpath 下的 *.jar
各个 ClassLoader 的层次自顶往下为启动类加载器、扩展类加载器、应用类加载器和自定义类加载器。其中,应用类加载器的双亲为扩展类加载器,扩展类加载器的双亲为启动类加载器。当系统需要使用一个类时,在判断类是否已经被加载时,会从当前底层类加载器进行判断。当系统需要加载一个类时,会从顶层类开始加载,依次向下尝试,直到到成功。
在这些些 ClassLoader 中,启动类加载器最为特别,它是完全由 C 代码实现的,并且在 Java 中没有对象与之对应。系统的核心类就是由启动类加载器进行加载的,它也是虚拟机的核心组件。扩展类加载器和应用类加载器都有对应的 Java 对象可供使用。
// 输出全部的类加载器
public class PrintClassLoaderTree {
public static void main(String[] args) {
ClassLoader classLoader = PrintClassLoaderTree.class.getClassLoader();
while (classLoader != null) {
System.out.println(classLoader);
classLoader = classLoader.getParent();
}
}
}
结果如下:
[email protected]
[email protected]
三、ClassLoader 的双亲委托模式
系统中的 ClassLoader 在协协同工作时,默认会使用双亲委托模式。即在类加载的时候,系统会判断当前类是否已经被加载,如果已经被加载,就会直接返回可用的类,否则就会尝试加载,在尝试加载时,会先请求双亲处理,如如果双亲请求失败,则会自己加载。
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); // (1)
if (c == null) {
long t0 = System.nanoTime();
try {
if (parent != null) { // (2)
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); // (3)
// 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;
}
}
(1) 当前 ClassLoader 试图查找该类是否已经被加载,如果已经被加载则直接返回。
(2) 如果没有被加载,则会请求其双亲加载(不是自己加载),如果双亲为 null 时,则使用启动类加载器加载。
(3) 如果双亲加载不成功,则由当前 ClassLoader 尝试加载。
参考:
本文转载至《实战JAVA虚拟机 JVM故障诊断与性能优化》
每天用心记录一点点。内容也许不重要,但习惯很重要!
以上是关于JVM 系列 ClassLoader的主要内容,如果未能解决你的问题,请参考以下文章
JVM深层系列「逆向ClassLoader加载机制」认识一下线程上下文类加载器实现
[jvm解析系列][九]类的加载过程和类的初始化。你的类该怎么执行?为什么需要ClassLoader?
Android 逆向类加载器 ClassLoader ( 类加载器源码简介 | BaseDexClassLoader | DexClassLoader | PathClassLoader )(代码片段