• 类加载过程


    1. 概述

      一个.java文件编译为.class文件后才可以被加载到虚拟机中运行和使用.

      虚拟机把描述类的.class文件加载到内存, 并对class文件进行验证、准备、解析和初始化后, 最终形成可以被虚拟机直接使用的Java类型, 这就是虚拟机的类加载机制.

    2. 类加载的时机

      类从加载到虚拟机内存中到卸载出内存为止。它的整个生命周期包括:加载、验证、准备、解析、初始化、使用和卸载7个阶段。

      类加载的顺序即按照此顺序开始,这些阶段通常互相交叉式地混合进行,通常在一个阶段的执行过程中激活下一个阶段。但是某些情况下解析阶段会在初始化后进行(比如在运行时动态的解析某些方法的符合引用,多态的使用等等);

      虚拟机规范严格规定了5种情况下必须对类进行初始化,这五种情况称为类的主动引用,除此之外的其他方式都不会触发类的初始化,称为被动引用

    2.1  主动引用

    1. 遇到new(创建对象)、getstatic(读取类静态变量)、putstatic(设置类静态变量)、invokestatic(调用静态方法)这四条字节码指令时,如果类未进行初始化,则触发其初始化;
    2. 使用 java.lang.Reflect 包的方法对类进行反射调用的时候;
    3. 当初始化一个类时,其父类还未初始化,则需要先触发父类的初始化; 接口初始化时,只有在真正用到父接口时才初始化父接口
    4. 当虚拟机启动时,虚拟机会先初始化要执行的主类
    5. 如果 java.lang.invoke,MethodHandle 实例最后解析结果为REF_getStatic、REF_putStatic、REF_invokeStatic的方法句柄时,若此类未进行初始化,则先触发其初始化。

    2.2  被动引用

    1. 通过子类调用父类的静态变量,若父类未进行初始化则会触发父类的初始化,而不会触发子类的初始化;
    2. 通过数组定义类引用;
    3. 引用常量

    3.  类加载过程

      类加载过程一般就是加载、验证、准备、解析、初始化五个阶段。

    3.1 加载

      加载阶段主要完成3件事:

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

    3.2  验证

      确保class文件的字节流包含的信息符合当前虚拟机的要求,不会为危害虚拟机的自身安全。

    1. 文件格式验证:直接操作字节流,保证输入的字节流能正确的解析并存储于方法区,格式上符合一个Java类型的要求;
    2. 元数据验证:对类的元数据进行语义校验,保证其描述的信息符合Java语言规范的要求;
    3. 字节码验证:通过数据流和控制流分析,确保语义是合法的,符合逻辑的;
    4. 符号引用验证:确保解析动作能正常执行,发生在将符号引用转化为直接引用的时候。

    3.3  准备

      为类变量分配内存空间,并设置类变量的初始值,这些变量的内存分配都在方法区中进行。

      初始化类的方法表。

      static final 修饰的变量直接初始化为指定的值。

    3.4  解析

      将常量池中一部分符号引用转化为直接引用的过程。

      符号引用:一组用来描述引用目标的符号。

      直接引用:直接指向目标的指针,相对偏移量或能间接定位到目标的句柄。

      在类加载的解析阶段将符号引用解析为直接引用的前提是:方法在程序真正运行之前就有一个可确定的方法调用版本,并且在 运行期间是不会改变的

      Java虚拟机提供了五种方法调用的字节码指令:

    1. invokestatic:调用静态方法
    2. invokespecial:调用构造方法,私有方法和父类方法
    3. invokevirtual:调用所有的虚方法
    4. invokeinterfafce:调用接口方法,运行时确定一个实现此接口的对象
    5. iinvokedynamic:运行时动态解析出调用点所引用的方法,然后执行该方法。

      能被 invokestatic 和 invokespecia 调用的方法都可以在解析阶段被转化为直接引用,这类方法被称为非虚方法(final 修饰的方法也是非虚方法),其他方法称为虚方法

      解析调用是一个静态的过程,在编译期间就可以完全确定。

      分派调用则可能是静态的也可能是动态的,还可以根据分派依据的宗量数分为单分派和多分派。

      重载:编译期,静态多分派,根据参数的静态类型确定方法的使用版本

      重写:运行期,动态单分派,根据对象的实际类型确定方法的使用版本

    3.5  初始化

      根据<clinit>方法为所有的类变量进行赋值,并执行静态代码块。

      <clinit>是由编译器自动收集类中的所有类变量的赋值动作(准备阶段JVM为类变量只分配了初始值)和静态语句块;

  • 相关阅读:
    什么是软件架构?
    子系统、框架与架构
    今天开始锻炼身体
    程序语言中基本数值类型的分类
    软件架构的作用
    软件架构要设计到什么程度
    软件架构视图
    更多资料
    How to:如何在调用外部文件时调试文件路径(常见于使用LaunchAppAndWait和LaunchApp函数)
    installshield卸载时提示重启动的原因以及解决办法
  • 原文地址:https://www.cnblogs.com/virgosnail/p/9609118.html
Copyright © 2020-2023  润新知