Java中的类加载

我们知道在运行Java程序时,首先需要把源代码编译成二进制文件也就是class文件,然后虚拟机才能执行。那虚拟机在执行class文件时,都进行了哪些步骤呢。下面我们将详细分享一下。当类也就是class文件被加载到虚拟机内存开始,到卸载出内存为止。它将要执行以下7个步骤:

  • 加载
  • 验证
  • 准备
  • 解析
  • 初始化
  • 使用
  • 卸载

下面我们看一下每一个阶段的详细流程。

  • 加载

加载也就是把class文件加载到虚拟机内存中。在加载时虚拟机需要完成3个方面的工作:

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

在加载完成后,虚拟机将这个二进制流存储在虚拟机的方法区中,并且通过在内存中生成的java.lang.Class对象可以访问方法区中的类型数据。

  • 验证

验证的目的是保证加载的class文件中的字节流是符合虚拟机要求的,并且不会威胁虚拟机的安全。在验证阶段虚拟机主要分为4个步骤来验证:

  1. 文件格式验证:主要的验证是验证二进制流是否符合Class文件的规范,并且是否能被当前版本的虚拟机处理。只有通过这个阶段的验证,虚拟机才会把二进制流存储到虚拟机内存的方法区中。
  2. 元数据验证:这个阶段主要验证的是字节码的描述信息是否符合Java语言规范,也就是说主要对类的元信息进行语法上的检查,保证符合Java语言规范的元信息。
  3. 字节码验证:这个阶段是最复杂的也是最耗时的验证,它主要目的是对类的方法体进行验证,校验类中的方法在运行时,不会做出威胁虚拟机的安全事件。
  4. 符号引用验证:目的是将虚拟机中的符号引用转化为直接引用,保证解析动作可以正常执行。
  • 准备

准备阶段的目的是为静态变量分配内存并设置静态变量的默认初始值。并且这个阶段只会为静态变量分配内存并不包括实例变量。实例变量将在对象实例化时随着对象分配在虚拟机的堆中。设置静态变量的默认初始值并不代表是复制操作。例如:

public static int value = 1;

在虚拟机准备阶段时,上述代码中的value并不会设置为1,而是0,因为这时还没有执行Java的赋值运算。不同的基本数据类型的默认初始值是不同的,下面是它们的区别:

正常在准备阶段静态变量的初始化都是默认值,也就是上述列表中的值,但也有例外的可能,如果一个静态变量是final变量,那么在准备阶段时,此变量将会被赋值为指定的值。

public static final int value = 1;

在上述代码中,在准备阶段就会把value设置为1。

  • 解析

解析就是虚拟机将常量池中的符号引用替换成直接引用的过程。在解析时主要分为下面的几种:

  1. 类或接口的解析
  2. 字段解析
  3. 类方法解析
  4. 接口方法解析
  • 初始化

初始化阶段主要的目的是为静态变量或者静态代码块执行赋值的操作。静态代码块中的语句只能访问定义在静态代码块之前变量,定义它之后的变量,在静态代码块中可以赋值,但不却不能访问。下面我们看一下具体的代码。

public static Test {
static {
i = 0; // 编译没有问题 虽然静态变量的定义在静态代码码之后但依然可以赋值
System.out.println(i); //编译出错 静态代码块只能访问在它之前定义的静态变量
}
static int i = 1;
}
发表评论
留言与评论(共有 0 条评论)
   
验证码:

相关文章

推荐文章

'); })();