ClassLoader是怎么工作的

Posted wangflower

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ClassLoader是怎么工作的相关的知识,希望对你有一定的参考价值。

ClassLoader是为Java的底层技术,它是为了来加载class文件的,负责将字节码形式的Class文件转换为内存形式的Class对象,这个加载的字节码可以是磁盘文件的class文件, 也可以是jar包中的class,也可以是服务调用中来自远程服务提供的字节流,字节码的本质是一个字节数组[]byte,但是有特定的复杂的内部格式

ClassLoader类似一个容器,里面装载了很多class对象,JVM运行实例中会存在多个ClassLoader,不同多ClassLoader会从不同的地方加载字节码文件,JVM本身内置了三个重要的ClassLoader

分别是BootstrapClassLoader,ExtensionClassLoader和AppClassLoader

BootstrapClassLoader负责加载JVM运行时核心类,这些类位于$JAVA_HOME/lib/rt.jar文件中,我们常用的jdk工具类库都在里面,如java.util. java.lang等等,这个ClassLoader是根加载器,是比较特殊的,由C语言编写。

ExtensionClassLoader负责加载JVM扩展类,一些以javax开头的jar包,如位于$JAVA_HOME/lib/ext/*.jar中的很多

AppClassLoader是直接面开发者的类加载器,它会去加载ClassPath中定义的指定路径中的jar包和目录,我们编写的代码以及使用的第三方jar包通常都是由它加载的

AppClassLoader可以由ClassLoader提供的静态方法getSystemClassLoader获取到,定义为系统类加载器

@CallerSensitive
public static ClassLoader getSystemClassLoader() {
initSystemClassLoader();
if (scl == null) {
return null;
}
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
checkClassLoaderPermission(scl, Reflection.getCallerClass());
}
return scl;
}

当我们执行Main方法时,第一个用户类的加载器就是AppClassLoader

技术图片

 

 

JVM运行时并不是一次性加载全部的类,它是按需加载,所以可以理解为延迟加载,比如在调用一个类的静态方法时,首先这个类肯定是需要被加载的,但是不会使用到这个类的实例字段,那么这个类的实例字段对应的类别Class就可以暂时不用去加载,只会去加载静态字段相关的类别,因为静态方法可以访问静态字段,实例字段的类别就是在实例化对象的时候才会去加载,并且 程序在运行过程中会遇到很多不认识的新类,这个时候就会去调用ClassLoader去加载,加载完的Class对象就会放在容器中,下次使用就不需要重新加载了,但是问题在于,对于一个不认识的新类,到底是由哪个ClassLoader去加载呢,JVM的策略是让调用者的Class对象的ClassLoader来加载当前的未知的类,遇到这个新的未知的类时,JVM肯定是在运行一个方法的调用,不管是静态方法还是实例方法,这个方法挂在哪个类上面,那么这类就是调用者Class对象,每个Class对象里面都有一个属性记录了当前的类是由谁加载的,因为ClassLoader的传递性,所有延迟加载的类都会由初始调用main方法的ClassLoader负责,那就是AppClassLoader

那么当AppClassLoader去加载Class时,遇到没有加载的系统类库怎么办,这个时候AppClassLoader必须将系统类库的加载工作交给BootstrapClassLoader和ExtensionClassLoader来做,也就是双亲委派原则

AppClassLoader在执行具体操作时,加载一个未知的类名,并不是立即去搜寻ClassPath(前面说过AppClassLoader只负责加载ClassPath下面的类库),它会将这个类名称交给ExtensionClassLoader来加载,ExtensionClassLoader在加载一个未知的类名时,它也并不是立即去搜寻ext路径,而是首先将类名称交给BootstrapClassLoader来加载,如果BootstrapClassLoader能够加载,那么ExtensionClassLoader就不用去搜索ext路径下的所有的jar包,最终交给AppClassLoader,如果已经加载了,那么AppClassLoader就不会去搜寻ClassPath下所有的jar包

三个ClassLoader之间形成了级联的父子关系,每个ClassLoader拿到一个类名时都尽量交给自己的父级去做,父级做不了才会自己干活,所有每个ClassLoader对象内部都会有一个parent属性,指向它的父加载器

public abstract class ClassLoader {

private static native void registerNatives();
static {
registerNatives();
}

// The parent class loader for delegation
// Note: VM hardcoded the offset of this field, thus all new fields
// must be added *after* it.
private final ClassLoader parent;

不同的 ClassLoader 之间也会有合作,它们之间的合作是通过 parent 属性和双亲委派机制来完成的。parent 具有更高的加载优先级。除此之外,parent 还表达了一种共享关系,
当多个子 ClassLoader 共享同一个 parent 时,那么这个 parent 里面包含的类可以认为是所有子 ClassLoader 共享的。这也是为什么 BootstrapClassLoader 被所有的类加载器视为祖先加载器,
JVM 核心类库自然应该被共享


 

以上是关于ClassLoader是怎么工作的的主要内容,如果未能解决你的问题,请参考以下文章

java的ClassLoader的getResources方法怎么使用

Java ClassLoader 在新线程(可运行)中使用时停止工作

ClassLoader工作机制

ClassLoader工作机制

ClassLoader工作机制

详细深入分析 ClassLoader 工作机制