JVM之加载java类

JVM之加载java类

【什么是类加载?】
工程文件中编写的.java文件存储着需要计算机执行的程序逻辑,.java文件经过Java编译器编译成拓展名为.class文件,.class文件中保存着Java代码经转换后的字节码指令。
当需要使用某个类时,java虚拟机将会加载它的.class文件,并创建对应的Class对象,将class文件加载到虚拟机的内存,这个过程就是类加载。

【类加载的基本过程】
JVM之加载java类

【类加载过程详解】

加载阶段
加载概念
加载,是指查找字节流,并且据此创建类的过程。
注意:字节流最常见的形式要属由 Java 编译器生成的 class 文件。除此之外,我们也可以在程序内部直接生成,或者从网络中获取例如网页中内嵌的小程序 Java applet)字节流等。这些不同形式的字节流,都会被加载到 Java 虚拟机中,成为类或接口。

类加载器
对于普通的类来说,JVM需要通过类加载器来完成查找字节流的过程。
所有的类加载器都继承于启动类加载器(java.lang.ClassLoader),而这些类加载器本身又需要另一个类加载器(如启动类加载器)加载至java虚拟机中,然后才能执行类加载。
注意:启动类加载器是有C++实现的,没有对应的java对象,因此在java中以null来指代。而其他的类加载器都有对应的java对象。

加载过程
类加载在执行加载请求时,采用双亲委派模型
即当一个类加载器接收到加载请求时,它会先将请求转发到父类加载器。在父类加载器没有找到所请求的类的情况下,该类加载器才会尝试去加载。

各类加载器负责范围
jdk9之前:
启动类加载器 负责加载最基础,最重要的类。比如JRE的lib目录下jar包中的类
扩展类加载器 负责加载相对次要但又通用的类,比如JRE的lib/ext目录下的jar包中的类(以及由系统变量 java.ext.dirs 指定的类)。
应用类加载器 负责加载应用程序路径下的类。默认情况下,应用程序中包含的类便是由应用类加载器加载的。
注意:应用类加载器的父类为扩展类加载器,扩展类加载器的父类为启动类加载器。

链接阶段

验证阶段
目的:保被加载类能够满足 Java 虚拟机的约束条件
通常来说,java编译器生成的类文件必然满足JVM的约束条件。

准备阶段
目的:
1.为被加载类的静态字段分配内存。
2.部分JVM还会在此阶段构造其他跟类层次相关的数据结构,比如用来实现虚方法的动态绑定的方法表。

解析阶段
在class文件被加载到JVM之前,这个类无法知道自己以及其他类的方法、字段所对应的具体地址。因此,每当需要引用这些成员时,java编译器会生成一个符号引用。(在运行阶段,这些符号引用一般能无歧义地定位到具体目标上)
解析阶段的目的,就是将这些符号引用解析成为实际引用。(如果符号引用指向一个未被加载的类,或字段、方法,那么解析将触发这个类的加载阶段)

初始化阶段

目的:为标记为常量值的字段赋值,以及执行方法。JVM会通过加锁确保类的方法只执行一次。当初始化完成,类才正式变成可执行状态。
补充
常量值:static final 字段,且其类型为基本类型或String类型。(其初始化有JVM直接完成)
<clinit>:除常量值以外的所有赋值操作(new),以及所有静态代码块中的代码,会被java编译器置于同一方法中,此方法即为<clinit>方法。

类初始化阶段触发汇总

  1. 当虚拟机启动时,初始化用户指定的主类
  2. 当遇到用以新建目标类实例的 new 指令时,初始化 new 指令的目标类
  3. 当遇到用以新建目标类实例的 new 指令时,初始化 new 指令的目标类
  4. 当遇到调用静态方法的指令时,初始化该静态方法所在的类
  5. 当遇到访问静态字段的指令时,初始化该静态字段所在的类
  6. 子类的初始化会触发父类的初始化
  7. 如果一个接口定义了 default 方法,那么直接实现或者间接实现该接口的类的初始化,会触发该接口的初始化
  8. 使用反射 API 对某个类进行反射调用时,初始化这个类
  9. 当初次调用 MethodHandle 实例时,初始化该MethodHandle 指向的方法所在的类