程序的生命周期
程序的生命周期是指应用程序启动到应用程序结束整个阶段的全过程。一段Java Code,大体分为:编译、类加载、运行、GC。
- 编译
Java语言的编译期其实是一段“不确定 ”的过程,因为可能是一个前端编译器把*.java文件转变为.class文件的过程;也可能是指JVM的后端运行期编译器(JIT编译器)把字节码转变为机器码的过程;还可能是指使用静态提前编译器(AOT编译器)直接把*.java文件编译成本地机器码的过程。但是在这里我们说的是第一类。也是符合我们大众对编译认知的。编译在这个时间段经历了哪些过程呢?
- 词法、语法分析
词法分析是将源代码的字符流转变为Token集合,而语法分析则是根据Token序列抽象构造语法树(ATS)的过程,ATS是一种用来描述程序代码语法结构的树形表示形式,语法树的每个节点都代表着程序代码中的一个语法结构,例如包、类型、修饰符、运算符、接口、返回值甚至代码注释都可以是一个语法结构。
- 填充符号表
完成了语法和词法分析之后,下一步就是填充符号表的过程,符号表中所登记的信息在编译的不同阶段都要用到。在这里延伸一下符号表的概念。符号表是什么呢?它是由一组符号地址和符号信息构成的表格,最简单的可以理解为哈希表的K-V值对的形式。为什么会用到符号表呢?符号表最早期的应用之一就是组织程序代码的信息。最初,计算机程序只是一串简单的数字,但程序员们很快发现使用符号来表示操作和内存地址(变量名)要方便得多。将名称和数字关联起来就需要一张符号表。随着程序的增长,符号表操作的性能逐渐变成了程序开发效率的瓶颈,为此从而诞生了许多提升序号表效率的数据结构和算法。至于所谓的数据结构和算法有哪些呢?大体说下:无序链表中的顺序查找、有序数组中的二分查找、二叉查找树、平衡查找树(在这我们主要接触到的是红黑树)、散列表(基于拉链法的散列表,基于线性探测法的散列表)。像Java中的java.util.TreeMap和java.util.HashMap分别是基于红黑树和拉链法的散列表的符号表实现的。这里提到的符号表的概念不再细说,感兴趣的可以查找相关资料。
- 语义分析
经过上两步之后,我们获得了程序代码的抽象语法树表示,语法树能表示一个正确的源代码抽象,但无法保证源程序是符合逻辑的,这时候语义分析登场了,它的主要任务就是对结构上正确的源程序进行上下文有关性质的审查。标注检查、数据及控制流分析、解语法糖是语义分析阶段的几个步骤,在这具体说下语法糖的概念。语法糖是指在计算机语言中添加的某种语法,这种语法对语言的功能并没有影响,但更方便程序员使用。Java中最常用的语法糖主要是泛型、变长参数、自从装箱/拆箱、遍历循环,JVM在运行时不支持这些语法,它们在编译阶段还原回简单的基础语法结构,这个过程也就是解语法糖。举个泛型擦除的例子,List和List在编译之后会进行泛型擦除,变成一样的原生类型List。
- 字节码生成
字节码生成是Javac编译过程的最后一个阶段,在这个阶段会把前面各步骤生成的信息转化成字节码写到磁盘中,还会进行了少量代码添加和转换的工作。实例构造器()方法和类构造器()方法(这里的实例构造器并不是指默认构造函数,