浅谈类的加载过程

我们编写的类.java文件,再被编译成.class文件储存成二进制字节码后,并不可以直接使用,必须经过类加载,一个类才可以被装载进运行时内存并被使用。因此理解类加载机制才能让我们更深刻地理解我们编写的java代码是如何一步一步的编译成.class文件,到如何在内存中正确的使用的过程。

package com.fanqiechaodan.classloader;

import com.fanqiechaodan.A;

/**
 * @date 2021/6/23 22:07
 * @declare
 */
public class ClassLoader {
    
    

    private static final int i = 10;

    public static void main(String[] args) {
    
    
        int b = 15;
        int sumByI = getSumByI(b);
        A a = new A(sumByI);
    }

    /**
     * 一个方法再内存中就有一块属于自己的栈帧区域
     *
     * @param b
     * @return
     */
    private static int getSumByI(int b) {
    
    
        return i+b;
    }
}

类的加载过程一共分为七大步:

  1. 加载:加载是类加载的第一个过程,在这个阶段,将完成三件事:
    1.1. 通过一个类的全限定类名获取该类的二进制流
    1.2.将二进制流中的静态储存结构转换未方法去运行时数据结构
    1.3. 再内存中生成该类的.class文件,作为该类的数据访问入口
  2. 验证:验证的目的是为了确保class文件的字节流中的信息不会危害到虚拟机,再该阶段主要完成以下四种验证:
    2.1.文件格式验证:如常量池中的常量是否有不被支持的类型
    2.2.元数据验证:对字节码描述的信息进行语义分析,例如:这个类是否有父类,是否继承了不可以被继承的类等
    2.3.字节码验证:是整个验证过程中最复杂的一个阶段,通过验证数据流和控制流的分析,确定程序语法是否正确,主要针对方法体的验证;例如:方法中的类型转换是否正确;跳转指令是否正确
    2.4.符号引用验证:这个东西再后面解析的过程中发生,主要是为了确保解析动作可以正常的执行
  3. 准备:准备阶段是为了类的静态变量分配内存,并将其初始化为默认值,这些内存都再方法区中进行分配,准备阶段不分配类中的实体对象内存,实体对象内存会在对象实例化时伴随着对象一起分配再堆内存中
  4. 解析:该阶段主要是完成符号的引用到直接引用转换动作,解析的动作并不一定再初始化动作之前,也可能再初始化动作之后
  5. 初始化:初始化是类加载的最后一步,主要是对类的静态变量初始化为指定的值,执行静态代码块,前面的四步类加载的过程,除了再加载阶段可以通过自定义的类加载器参与之外,其他的完全是有虚拟机主导和控制
  6. 执行/使用:过了初始化阶段,才开始真正的执行类中定义的java代码
  7. 销毁:只有再以下四种情况下类才会销毁:
    7.1. 执行了System.exit()
    7.2.程序正常执行结束
    7.3.程序再执行的过程中遇到了异常或者错误而终止
    7.4.由于操作系统出现的错误而导致java虚拟机进程终止

类加载流程图:在这里插入图片描述
注意:当前类再运行的过程中如果使用到其他的类时,才会逐步的去加载使用到的类,jar/war包里面的类不是一次性全部加载的,是使用到时才会进行加载;这种加载策略可以大大的节省程序启动的时间,再多态的情况下,如果使用到的类有很多子类/或者很多实现类,只有当代码执行到使用到引用类的时候才会知道具体有什么实现,加载具体的子类/实现类

Guess you like

Origin blog.csdn.net/qq_43135259/article/details/118163689