类加载器

1 类装载流程  : 加载,链接,初始化。

  (1)加载 装载类的第一阶段,获取类的二进制流,转化为方法区数据结构,并在java堆中生成对应的java.lang.Class对象。

  (2)链接

    第一步 各种验证

    文件格式验证:保证class格式正确。包括是否以0xCAFEBABE开头,版本号是否合理。

    元数据验证:父类是否存在,是否继承final类,非抽象类是否实现了所有抽象方法等。

    字节码验证:运行检查,栈数据类型和操作码数据参数是否吻合,跳转指令到合理位置(根据字节码偏移量),目的是确保class能正确执行。

    引用符号验证:常量池中描述类是否存在,访问的方法是否存在以及是否有权限(private,protect等权限);

    第二步 准备

    分配内存,在方法区中为类设置值。例如 public static int i=1; 在准备阶段会被设置值0,在初始化才设置为1;如果加上final,在准备阶段就设置为1.

    第三步 解析 

    符号引用替换为直接引用。符号引用以一组符号来描述所引用的目标,编译的时候并不一定知道目标的内存地址,目标也不一定被加载入内存;直接引用指的指针或者地址偏移量,引用对象一定在内存。

    第四步 初始化

    执行类构造器,包括赋值语句和static{}语句,保证父类的构造器被调用。

2 classLoader的本质

  classLoader是一个抽象类,实例将读入java字节码将类装载到jvm里,可以定制来满足不同的字节码流获取方式,负责类装载过程中的加载阶段。多个classloader分层级,没有继承,是组合关系。

   Bootstrap ClassLoader 启动classloader  (主要负责jdk_home/lib目录下的核心 api 或 -Xbootclasspath 选项指定的jar包装入工作,如:rt.jar、resources.jar、charsets.jar等) ,但不继承自ClassLoader,因为它不是一个普通的Java类,底层由C++编写,已嵌入到了JVM内核当中,当JVM启动后,Bootstrap ClassLoader也随着启动,负责加载完核心类库后,并构造Extension ClassLoader和App ClassLoader类加载器。

     Extension ClassLoader 扩展classloader (主要负责jdk_home/lib/ext目录下的jar包或 -Djava.ext.dirs 指定目录下的jar包装入工作)

     App ClassLoader 应用/系统classloader(主要负责java -classpath/-Djava.class.path所指的目录下的类与jar包装入工作)

     Custom ClassLoader 自定义classloader(在程序运行期间, 通过java.lang.ClassLoader的子类动态加载class文件, 体现java动态实时类装入特性)

   *自底向上检查是否已经加载,逐级询问是否有某个类;如果自底向上都没加载,那么自顶向下进行加载过程,直到加载成功那一层为止。这两个过程是双亲模式,委托parent,双亲委派模型提供了优先级的层次关系和隔离型。存在问题:顶层classloader不能加载底层classloader。使用双亲委派模型的好处是避免重复加载,

   *每个classloader都维护一个自己的命名空间,同一个空间里不能出现两个同名的类。判断两个class是否相同,不仅看类名是否相同,还看加载器是否是同一个,同一个字节码文件被两个加载器加载,也会认为是两个类。

   *Thread.setContextClassLoader() 上下文加载器,用于解决顶层classloader无法访问底层classloader的类的问题,在顶层classloader中,传入底层classloader的实例。这个加载器是个角色,上面四个加载器都可以充当这个角色。   

  * 也可以破坏双亲模式,自底向上,检查了没有就加载。自定义类加载器时不需要在自己写双亲委派的逻辑,因此不鼓励重写loadClass方法,而推荐重写findClass方法

  * 当一个class被替换以后,系统无需重启替换的类立刻生效,即为热替换。   

原文地址:https://www.cnblogs.com/lkdirk/p/6432693.html