class装载验证流程
加载
装载类的第一个阶段,取得类的二进制字节流,并转换为方法区的数据结构,在Java堆中生成对应的java.lang.Class对象。
链接
验证
- 文件格式的验证,是否以0xCAFEBABE开头,版本号是否合理等。
- 元数据验证,是否继承父类,是否有继承final类等。
- 字节码验证(复杂),运行检查,栈数据类型和操作码数据参数吻合,跳转指令指定到合理的位置等。
- 符号引用验证,常量池中描述类是否存在,访问的方法或字段是否存在且应有足够的权限。
- 目的是为了保证Class流格式的正确性。
准备
在方法区中给类的类变量(static修饰)分配内存,然后初始化其值,如果类变量是常量,则直接赋值为该常量值否则为java类型的默认的零值。
public class Test {
public static int a = 1;//准备阶段a会被赋值为0,初始化时才会被赋值为1
public static final int a = 1;//准备阶段a会被赋值为1,并且a永远不会变
}
解析
解析过程中会把类的二进制数据中的符号引用替换为直接引用。符号引用指引用对象不一定被加载,直接引用指针或地址偏移量,引用对象一定在内存。
/**
*解析前str为符号引用,即在Class的常量池中个字符串,假如没有继承就为默认父类java.lang.Object,不能被直接使用。
*解析将str的符号引用转为一个指针,指向str在栈中的内存位置。
*/
public class Test {
public String str;//
}
初始化
初始化即执行构造器,为static变量赋值、执行static{}语句。如果这个类有父类,并且这个父类没有被初始化,则先初始化父类。但是一个父接口并不会因为它的子接口或者实现类的初始化而初始化。只有当程序首次使用特定接口的静态变量时,才会导致接口的初始化。另外,初始化是线程安全的。
- 类加载的顺序如果是一个类的话,加载的顺序是:
- 类的静态属性
- 类的静态代码块
- 类的非静态属性
- 类的非静态代码块
- 构造方法
- 如果一个类有父类,加载的顺序是:
- 父类的静态属性
- 父类的静态代码块
- 子类的静态属性
- 子类的静态代码块
- 父类的非静态属性
- 父类的非静态代码块
- 父类的构造方法
- 子类的非静态属性
- 子类的非静态代码块
- 子类的构造方法。
类装载器ClassLoader
ClassLoader是一个抽象类,它的实例读入java字节码转为类的字节流装载到JVM中,ClassLoader是可以定制的,满足不同的字节码流获取方式(网络中,文件中等等),ClassLoader负责类装载过程中的加载阶段。
ClassLoader的重要方法
public Class <?> loadClass(String name) throws ClassNotFoundException //加载类的入口方法,调用该方法完成类的显式加载。通过对该方法的重新实现,我们可以完全控制和管理类的加载过程
protected final Class<?>defineClass(byte[] b, int off, int len)//定义一个类,不公开调用,,它接收以字节数组表示的类字节码,并把它转换成 Class 实例,该方法转换一个类的同时,会先要求装载该类的父类以及实现的接口类。
protected Class<?>findClass(String name) throws ClassNotFoundException//loadClass回调该方法,自定义ClassLoader的推荐做法
protected final Class<?>findLoadedClass(String name) //寻找已经加载的类
getSystemClassLoader:Java2 中新增的方法。该方法返回系统使用的 ClassLoader。可以在自己定制的类加载器中通过该方法把一部分工作转交给系统类加载器去处理。
ClassLoader分类
BootStarp ClassLoader (启动ClassLoader)
Extension ClassLoader (扩展ClassLoader)
App ClassLoader (应用ClassLoader)
Custom ClassLoader (自定义ClassLoader)
注意:Custom ClassLoader、Extension ClassLoader和App ClassLoader都必须继承自java.lang.ClassLoader类,但是Bootstrap ClassLoader不用继承自ClassLoader,因为它不是一个普通的Java类,底层由C++编写,已嵌入到了JVM内核当中,当JVM启动后,Bootstrap ClassLoader也随着启动,负责加载完核心类库后,并构造Extension ClassLoader和App ClassLoader类加载器。
JDK中ClassLoader默认设计模式-双亲模式
主要是两点:一是自底向上检查类是否已经被加载,二是自顶向下尝试加载类。
使用类的过程:我们自己写的类都加载在App ClassLoader中,当我们需要找一个类时,需要在当前的ClassLoader中找,即是在App ClassLader中查找,没有找到,就向上检查Extension ClassLoader,如果在 Extension ClassLoader中没有找到,继续向上检查Boostrap ClassLoader,在这三次检查中只要找到这个class,就停止向上检查,如果都没找到,就会尝试自顶向下进行加载类,如果Bootstrap ClassLoader加载成功,就返回这个class,没加载成功,就依次向下加载,知道加载成功,如果它们都没有加载到这个class时,则抛出ClassNotFoundException异常。否则将这个找到的类生成一个类的定义,并将它加载到内存当中,最后返回这个类在内存中的Class实例对象。
虽然双亲模式是默认的模式,但不是必须这么做,Tomcat的WebappClassLoader 就会先加载自己的Class,找不到再委托parent。
热替换
当一个class被替换后,系统无需重启,替换的类立即生效。