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

ClassLoader是怎么工作的

 

 

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 核心类库自然应该被共享

 

上一篇:《从JDK源码级别彻底剖析JVM类加载机制》


下一篇:java虚拟机详细图解10--JVM类加载机制及类加载过程