类加载机制--类的加载子系统

参考:《深入理解Java虚拟机第三版》

​ 《宋红康JVM教程》

前言:JVM虚拟机运行的是字节码文件,一个.java文件通过编译变成一个.class字节码文件,.class字节码文件才是JVM虚拟机需要的文件,但是.class文件中的类是什么时候被加载,如何被加载的呢?本文便详细介绍这几个问题。

一、类的生命周期

类被加载的到虚拟机内存中开始,到卸载出内存结束,一共经过下列过程:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-THymkMMl-1593854884291)(img/1.png)]

二、类的加载过程

1.加载

在加载阶段,虚拟机需要完成以下三件事情:

  • 通过一个类的全限定明获取定义此类的二进制字节流;
  • 将这个字节流所代表的的静态存储结构转化为方法区的运行时数据;
  • 在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口
2.链接
2.1验证
  • 目的在于确保Class文件的字节流中包含信息符合当前虚拟机要求,保证被加载类的正确性,不会危害虚拟机自身安全。
  • 主要包括四种验证,文件格式验证,源数据验证,字节码验证,符号引用验证。
2.2准备

准备阶段是正式为类变量分配内存并设置类变量初始值的阶段,这个时候进行内存分配的仅包括类变量,就是被static修饰的变量,不包括实例变量,实例变量的初始化过程不在这个阶段,例如:

public static int value = 123;

那么变量value在准备阶段过后的值便是0。把value的值变成123的阶段是初始化阶段。

那么如果是如下代码呢??

public static final int value = 123;

在准备阶段value的值已经是123了,因为被final修饰的static,在编译的时候就已经分配,准备阶段会显示初始化。

基本数据类型的零值:

在这里插入图片描述

2.3解析

解析阶段是虚拟机将常量池内的符号引用转换为直接引用的过程

3.初始化

在初始化阶段,才真正的去执行类中定义的Java代码,在之前准备阶段,对不被final修饰的static修饰的变量已经赋过初始值,而在初始化阶段,则根据程序主观计划去初始化变量和其他资源。初始化阶段是执行类构造器()方法的过程

  • ()方法是由编译器自动收集类中的所有类变量的赋值动作和静态代码块中的语句合并而来。编译器收集的顺序是语句在源文件中出现的顺序决定的,静态语句块只能访问到定义在静态语句块之前的变量,定义在它之后的变量,静态语句块可以赋值,但是不能访问

《深入理解JVM虚拟机第三版》中关于client方法第一条是如上描述的,可以通过以下程序来理解:

/**
 * @author 四五又十
 * @version 1.0
 * @date 2020/7/3 16:28
 */
public class demo1 {

    static {
        num = 2;
    }

    private static int num = 1;

    public static void main(String[] args) {
        //输出为1
        System.out.println(num);
    }
}

初看如上的程序,读者可能会觉得有点问题,因为num变量定义在静态代码块之后,但这样是可以通过编译的,因为在准备阶段已经将static修饰的变量赋予初值0,那么在静态代码块中可以访问也就不是什么很奇怪的事情

接着看如下代码:

/**
 * @author 四五又十
 * @version 1.0
 * @date 2020/7/3 16:28
 */
public class demo1 {

    static {
        num = 2;
        System.out.println(num);		//报错
    }

    private static int num = 1;

    public static void main(String[] args) {
        System.out.println(num);
    }
}

这段代码在static静态代码块中加入了访问num的语句,这样编译器会报错

Error:(14, 28) java: 非法前向引用

那么这两段程序很好的理解了,在上述描述中:静态语句块只能访问到定义在静态语句块之前的变量,定义在它之后的变量,静态语句块可以赋值,但是不能访问

  • ()方法对于类或接口来说不是必要的,如果一个类没有静态语句块,也没有对类变量的复制操作,那么编译器不会为这个类生成client方法

这点可以在idea中安装jclasslib插件,反编译字节码文件

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

在这里插入图片描述

当编译好一个文件时,可以打开视图

在这里插入图片描述

在这里插入图片描述

  • clinit()不同于类的构造器。(关联:构造器是虚拟机视角下的init())若该类具有父类,jvm会保证子类的clinit()执行前,父类的clinit()已经执行完毕

  • 虚拟机必须保证一个类的clinit()方法在多线程下被同步加锁。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章