类加载机制

七个阶段:加载、验证、准备、解析、初始化、使用、卸载。

加载

将字节码从各个位置(网络、磁盘等)转化为二进制字节流加载到内存中,接着会为这个类在 JVM 的方法区创建一个对应的 Class 对象,这个 Class 对象就是这个类各种数据的访问入口。

验证

  • **JVM规范校验。**JVM 会对字节流进行文件格式校验,判断其是否符合 JVM 规范,是否能被当前版本的虚拟机处理。例如:文件是否是以 0x cafe bene开头,版本号是否在当前虚拟机处理范围之内。
  • **代码逻辑校验。**JVM 会对代码组成的数据流和控制流进行校验,确保 JVM 运行该字节码文件后不会出现致命错误。例如参数类型错误、返回值错误等。

准备

为类变量分配内存并初始化。

  • 内存分配的对象:「类变量」指的是被 static 修饰的变量,而其他所有类型的变量都属于「类成员变量」。在准备阶段,JVM 只会为「类变量」分配内存,而不会为「类成员变量」分配内存。「类成员变量」的内存分配需要等到初始化阶段才开始。
  • 初始化的类型:为变量赋予 Java 语言中该数据类型的零值,而不是用户代码里初始化的值。如果一个变量是常量(被 static final 修饰)的话,那么在准备阶段就直接赋值。

解析

将常量池内的符号引用替换为直接引用的过程。

初始化

根据语句执行顺序,对类对象进行初始化。主要工作是为静态变量赋程序设定的初值。

  1. 使用new字节码指令创建类的实例,或者使用getstatic、putstatic读取或设置一个静态字段的值(放入常量池中的常量除外),或者调用一个静态方法的时候,对应类必须进行过初始化。
  2. 通过java.lang.reflect包的方法对类进行反射调用的时候,如果类没有进行过初始化,则要首先进行初始化。
  3. 当初始化一个类的时候,如果发现其父类没有进行过初始化,则首先触发父类初始化。
  4. 当虚拟机启动时,用户需要指定一个主类(包含main( )方法的类),虚拟机会首先初始化这个类。
  5. 使用jdk1.7的动态语言支持时,如果一个java.lang.invoke.MethodHandle实例最后的解析结果REF_getStatic、REF_putStatic、RE_invokeStatic的方法句柄,并且这个方法句柄对应的类没有进行初始化,则需要先触发其初始化。

使用

从入口方法开始执行用户的程序代码。

卸载

销毁创建的 Class 对象,最后负责运行的 JVM 退出内存。

两个区别

  • Class.forName 除了将类的.class文件加载到jvm中之外,还会对类进行解释,执行类中的static块
    • 内部实际调用的方法是 Class.forName(className,true,classloader),第2个boolean参数表示类是否需要初始化。
    • Class.forName()方法实际上也是调用的CLassLoader来实现的。
  • ClassLoader 只将.class文件加载到jvm中,不会执行static中的内容,只有在 newInstance 才会去执行static块
    • 实际调用的方法是 ClassLoader.loadClass(className,false),第2个 boolean参数,表示目标对象是否进行链接