.class文件需要被ClassLoader加载到JVM内存中的Method Area形成一个Class实例。
public final class Class<T> {
//不含PacakageName
public String getSimpleName();
//完整类名,包含PackageName
public String getName();
public Field[] getFields() throws SecurityException;
public Method[] getMethods() throws SecurityException;
.......
//获取ClassLoader
public ClassLoader getClassLoader();
}
public abstract class ClassLoader {
public Class<?> loadClass(String name) throws ClassNotFoundException;
public final ClassLoader getParent();
public URL getResource(String name);
public InputStream getResourceAsStream(String name);
....
}
JVM中默认有三个ClassLoader
Bootstrap ClassLoader
C语言编写,加载sun.boot.class.path下的类,包括resource.jar、rt.jar、charsets.jar等核心jar,可通过-Xbootclasspath指定其值, jinfo pid | grep boot可查看sun.boot.class.path值
Extension ClassLoader
sun.misc.Launcher.ExtClassLoader, rt.jar中定义,继承自java.lang.ClassLoader,加载java.ext.dirs目录下的jar,一般为$JAVA_HOME/jre/lib/ext/
App ClassLoader
sun.misc.Launcher.AppClassLoader, rt.jar中定义,继承自java.lang.ClassLoader,加载java.class.path目录下的jar, 也就是启动JVM时通过-classpath指定的jar,我们自定义的类基本都是通过AppClassLoader加载。
这些ClassLoader之间具有父子关系,AppClassLoader父亲是ExtClassLoader, ExtClassLoader父亲是Bootstrap ClassLoader,自定义ClassLoader父亲是AppClassLoader,可通过ClassLoader.getParent()获取父ClassLoader.
class加载过程
一个class只有在被使用时才会加载进内存,JVM先自底向上搜寻ClassLoader,看是否有ClassLoader已加载此类,如果没有加载,则采用双亲委派模式进行加载,即自底向上委托加载,只有其父ClassLoader无法加载时,子ClassLoader才会加载,因此首先会递归到顶部Bootstrap ClassLoader,然后再回溯至第一个可成功加载的ClassLoader,如果无ClassLoader成功加载则抛出ClassNotFoundException。
双亲委派模式可避免重复加载,当父ClassLoader加载了该类的时候,子ClassLoader不必再加载一次。此外这种委托模式更安全,双亲委派可避免那我们使用自定义的String来动态替代java核心api中定义的类型,因为String已经在启动时就被引导类加载器(Bootstrcp ClassLoader)加载,所以用户自定义的ClassLoader永远也无法加载一个自定义String。
class的ClassLoader可通过Class.getClassLoader()获取,在JVM启动参数中添加 -verbose:class 可查看类从那个jar包加载的。
ClassLoader加载.class时需经过以下步骤:
1、找到 .class 文件并把这个文件包含的字节码加载到内存中
2、分三步,字节码验证,以确保格式正确、行为正确;class类中定义的字段、方法和实现接口所必须的数据结构及相应的内存分配;最后的符号表的链接,装入类所引用的其他所有类
3、类中静态属性和初始化赋值,以及静态块的执行等
Custom ClassLoader
有时我们需要的类并不在classpath下,比如在网路上或者说有版本冲突时在自定义路径上,此时可自定义ClassLoader,指定class的路径,从对应路径上读取.class文件转换成byte[]进行加载。
自定义ClassLoader分两步:
1、继承java.lang.ClassLoader
2、重写父类的findClass方法