Hello World

类加载过程

1. 概述

  一个.java文件编译为.class文件后才可以被加载到虚拟机中运行和使用.

  虚拟机把描述类的.class文件加载到内存, 并对class文件进行验证、准备、解析和初始化后, 最终形成可以被虚拟机直接使用的Java类型, 这就是虚拟机的类加载机制.

2. 类加载的时机

  类从加载到虚拟机内存中到卸载出内存为止。它的整个生命周期包括:加载、验证、准备、解析、初始化、使用和卸载7个阶段。

  类加载的顺序即按照此顺序开始,这些阶段通常互相交叉式地混合进行,通常在一个阶段的执行过程中激活下一个阶段。但是某些情况下解析阶段会在初始化后进行(比如在运行时动态的解析某些方法的符合引用,多态的使用等等);

  虚拟机规范严格规定了5种情况下必须对类进行初始化,这五种情况称为类的主动引用,除此之外的其他方式都不会触发类的初始化,称为被动引用

2.1  主动引用

  1. 遇到new(创建对象)、getstatic(读取类静态变量)、putstatic(设置类静态变量)、invokestatic(调用静态方法)这四条字节码指令时,如果类未进行初始化,则触发其初始化;
  2. 使用 java.lang.Reflect 包的方法对类进行反射调用的时候;
  3. 当初始化一个类时,其父类还未初始化,则需要先触发父类的初始化; 接口初始化时,只有在真正用到父接口时才初始化父接口
  4. 当虚拟机启动时,虚拟机会先初始化要执行的主类
  5. 如果 java.lang.invoke,MethodHandle 实例最后解析结果为REF_getStatic、REF_putStatic、REF_invokeStatic的方法句柄时,若此类未进行初始化,则先触发其初始化。

2.2  被动引用

  1. 通过子类调用父类的静态变量,若父类未进行初始化则会触发父类的初始化,而不会触发子类的初始化;
  2. 通过数组定义类引用;
  3. 引用常量

3.  类加载过程

  类加载过程一般就是加载、验证、准备、解析、初始化五个阶段。

3.1 加载

  加载阶段主要完成3件事:

  1. 通过类的全限定名来获取定义此类的二进制字节流
  2. 将字节流所代表的静态存储结构转化为方法区的运行时数据结构
  3. 在内存中生成一个代表此类的 java.lang.Class 对象,作为方法区这个类的各种数据的访问入口。

3.2  验证

  确保class文件的字节流包含的信息符合当前虚拟机的要求,不会为危害虚拟机的自身安全。

  1. 文件格式验证:直接操作字节流,保证输入的字节流能正确的解析并存储于方法区,格式上符合一个Java类型的要求;
  2. 元数据验证:对类的元数据进行语义校验,保证其描述的信息符合Java语言规范的要求;
  3. 字节码验证:通过数据流和控制流分析,确保语义是合法的,符合逻辑的;
  4. 符号引用验证:确保解析动作能正常执行,发生在将符号引用转化为直接引用的时候。

3.3  准备

  为类变量分配内存空间,并设置类变量的初始值,这些变量的内存分配都在方法区中进行。

  初始化类的方法表。

  static final 修饰的变量直接初始化为指定的值。

3.4  解析

  将常量池中一部分符号引用转化为直接引用的过程。

  符号引用:一组用来描述引用目标的符号。

  直接引用:直接指向目标的指针,相对偏移量或能间接定位到目标的句柄。

  在类加载的解析阶段将符号引用解析为直接引用的前提是:方法在程序真正运行之前就有一个可确定的方法调用版本,并且在 运行期间是不会改变的

  Java虚拟机提供了五种方法调用的字节码指令:

  1. invokestatic:调用静态方法
  2. invokespecial:调用构造方法,私有方法和父类方法
  3. invokevirtual:调用所有的虚方法
  4. invokeinterfafce:调用接口方法,运行时确定一个实现此接口的对象
  5. invokedynamic:运行时动态解析出调用点所引用的方法,然后执行该方法。

  能被 invokestatic 和 invokespecia 调用的方法都可以在解析阶段被转化为直接引用,这类方法被称为非虚方法(final 修饰的方法也是非虚方法),其他方法称为虚方法

  解析调用是一个静态的过程,在编译期间就可以完全确定。

  分派调用则可能是静态的也可能是动态的,还可以根据分派依据的宗量数分为单分派和多分派。

  重载:编译期,静态多分派,根据参数的静态类型确定方法的使用版本

  重写:运行期,动态单分派,根据对象的实际类型确定方法的使用版本

3.5  初始化

  根据<clinit>方法为所有的类变量进行赋值,并执行静态代码块。

  <clinit>是由编译器自动收集类中的所有类变量的赋值动作(准备阶段JVM为类变量只分配了初始值)和静态语句块;

posted @ 2018-09-08 14:21  小小忧愁米粒大  阅读(164)  评论(0编辑  收藏  举报
瞅啥瞅,好好看书