类加载:字节码—>内存
Java类的加载流程是一个复杂但有序的过程,它确保了类文件能够被正确地加载到Java虚拟机(JVM)中,并被正确地初始化和使用。这个过程主要包括以下几个阶段:
1. 加载(Loading)
加载阶段是类加载过程的第一个阶段。在这个阶段,JVM通过类加载器(ClassLoader)完成以下三件事情:
- 通过一个类的全限定名(包括包名和类名)来获取定义此类的二进制字节流。
- 将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构。
- 在内存中生成一个代表这个类的
java.lang.Class
对象,作为方法区这个类的各种数据的访问入口。
2. 验证(Verification)
验证阶段是对加载的字节码进行验证,以确保其符合Java虚拟机规范。验证过程包括四个主要阶段:
- 文件格式验证:验证字节流是否符合Class文件格式的规范,并且能够被当前版本的虚拟机处理。
- 元数据验证:对类的元数据信息进行语义校验,如检查类是否有父类(除了
java.lang.Object
),类中的字段、方法是否与父类产生矛盾等。 - 字节码验证:通过数据流分析和控制流分析,确定程序语义是合法的、符合逻辑的。
- 符号引用验证:对类自身以外的各类信息进行匹配性校验,确保类中使用的外部资源是有效和可访问的。
3. 准备(Preparation)
准备阶段是为类的静态变量分配内存空间,并设置默认初始值。这些内存都在方法区分配。需要注意的是,这里设置的初始值是数据类型的默认值(如int
的默认值是0,而不是代码中可能指定的其他值),除非变量被声明为final
且同时被初始化,此时将直接赋予指定的值。
4. 解析(Resolution)
解析阶段是将常量池中的符号引用替换为直接引用的过程。符号引用是以一组符号来描述所引用的目标,而直接引用则是直接指向目标的指针、相对偏移量或者是一个能间接定位到目标的句柄。
5. 初始化(Initialization)
初始化阶段是类加载的最后一个阶段,主要负责执行类的初始化代码。这包括静态字段赋值的动作,以及执行类定义中的静态初始化块内的逻辑。如果类中有多个静态初始化块或静态字段赋值操作,它们将按照在源文件中出现的顺序执行。
6. 使用(Usage)和卸载(Unloading)
虽然这两个阶段不是类加载过程的一部分,但它们是类生命周期中的重要环节。在初始化之后,类的实例可以被创建,类的静态方法和变量可以被访问和使用。当类不再被使用时,JVM会卸载这个类,回收其占用的资源。
编译:源码—>字节码
源码生成字节码的流程通常被称为编译过程,特别是在Java等编译型语言中。这个过程将源代码(如.java文件)转换成一种中间代码形式——字节码(.class文件),这种字节码是Java虚拟机(JVM)能够直接识别和执行的。以下是源码生成字节码的主要步骤:
- 词法分析(Lexical Analysis):
- 编译器首先读取源代码文件,将其分解成一系列的标记(tokens)或词法单元。这些标记包括关键字、标识符、运算符、分隔符等。
- 语法分析(Syntax Analysis):
- 在词法分析的基础上,编译器将标记组合成语法结构,如表达式、语句和块等,并构建出一个语法树(parse tree)来表示源代码的结构。
- 语义分析(Semantic Analysis):
- 编译器对语法树进行进一步的分析,检查语法结构是否符合语言规范,进行类型检查,确认变量的作用域等。这一步还包括符号表的生成,用于存储变量、函数等标识符的信息。
- 中间代码生成:
- 在一些编译器的设计中,这一步可能会将语法树转换成一种更易于优化的中间代码表示形式,如中间表示(IR)或抽象语法树(AST)。然而,在Java编译器的典型流程中,这一步可能会与字节码生成紧密结合,不显式生成中间代码。
- 字节码生成(Bytecode Generation):
- 编译器将经过语义分析后的源代码(或中间代码)转换成字节码。字节码是一种与平台无关的二进制代码,它包含了一系列由JVM指令组成的操作码和操作数。这些指令可以被JVM的解释器或即时编译器(JIT Compiler)执行。
- 优化(Optimization)(可选,但常见):
- 在生成字节码之前或之后,编译器可能会进行一些优化操作,以提高程序的执行效率。这些优化可以包括代码重排、循环优化、内联函数等。
- 输出字节码文件:
- 最终,编译器将生成的字节码写入到一个或多个.class文件中。这些文件包含了类的定义、字段、方法以及实现这些方法的字节码指令。