深入理解Java虚拟机——Java虚拟机类加载机制

深入理解Java虚拟机——Java虚拟机类加载机制

概述

JVM把描述类的数据从class文件加载到内存,并对数据进行校验,转换解析和初始化,最终形成可使用的Java类型,这就是虚拟机的类加载机制。

在Java语言里,类的加载、连接和初始化过程都是在程序运行期间进行的。

Java动态加载的特性使得应用程序可以在运行时从网络上或者别的地方加载二进制流作为程序代码的一部分。

类加载时机

类的生命周期:

graph LR
加载-->验证
验证-->准备
准备-->解析
解析-->初始化
初始化-->使用
使用-->卸载

需要对类进行初始化的情况(在这之前必须进行加载、验证、准备):

  1. 遇到new、getstatic、putstatic、invokestatice四条指令时。
  2. 使用java.lang.reflect对类进行反射调用时。
  3. 初始化一个类时,必须先初始化区它的父类。(接口除外)
  4. main()方法所在的主类在虚拟机启动时就要初始化。
  5. 动态语言

这几种情况称为“对类的主动引用”。

非主动引用的情况有
1. 通过子类引用父类的静态变量,不会引起子类的初始化。
2. 通过数组引用类,不会触发该类的初始化:MyClass[] a = new MyClass[5]。
3. 常量在编译阶段被存入调用类的常量池中,引用类的常量不会触发该类的初始化。

类加载过程

加载

(1)通过类的全限定名来获取定义此类的二进制字节流。

(2)将字节流所代表的静态存储结构转换为方法区的运行时数据结构。

(3)在内存中生成一个代表此类的Class对象,作为方法区这个类的各种数据结构的访问入口。

Class对象不一定存放在堆中;对于Hotpot虚拟机而言,Class对象存放在方法区里面。

数组类本身不通过类加载器加载,他是由JVM直接创建的。

验证

这一阶段的目的是为了确保Class文件中的字节流包含的信息是符合虚拟机要求的。同时也是为了确保虚拟机的安全性。

  1. 文件格式验证(魔数、版本..)
  2. 元数据验证(语义分析、final、抽象方法的实现..)
  3. 字节码验证(数据类型的匹配..)
  4. 符号引用验证(验证类中引用其他的类的符号引用是否有效..)

准备

准备阶段是正式给类变量在方法区中分配内存,同时设置类变量的初始值。

这里的初始值是指0、false、null;这里的类变量是指static修饰的变量,即不包括实例变量;针对常量会在这里初始化为常量值。

解析

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

符号引用:字面量、跟虚拟机实现的内存布局无关。
直接引用:可以是指针、相对偏移量、句柄,跟具体的虚拟机实现有关。

符号引用的目标不一定已经加载到内存;而直接引用的目标必定已经存在内存中。

虚拟机规范未规定解析阶段的具体时间;但当这个符号引用被使用前必须经过解析。

符号引用包括:类或接口、字段、类方法、接口方法、方法类型、方法句柄和调用点限定符

初始化

初始化是类加载的最后一步,这阶段主要执行类变量的赋值,赋值的顺序由源码中赋值语句的顺序决定。

经过以下的初始化后,a的值为0:

class Demo {
    static {
        a = 1;
    }

    static int a = 0;  //后赋值的会覆盖前面赋值的
}

初始化阶段是执行类构造器()的过程,它是由编辑器收集所有类变量的赋值动作和静态语句块static{}合并产生的。

  • ()方法不需要显式调用父类的clinit,虚拟机会保证在子类的clinit方法执行之前,父类的clinit方法已经执行完毕。
  • clinit方法不是必需的。
  • 虚拟机会保证类的clinit方法在多线程环境中被正确地加锁、同步。

猜你喜欢

转载自blog.csdn.net/mingC0758/article/details/81150979
今日推荐