《深入理解Java虚拟机》读书笔记6--编译期优化

说起Java语言的编译期,它可能是指编译器把Java源码文件转变为Class字节码文件的过程,也可能是指虚拟机在运行时把字节码转变为机器代码的过程(JIT编译器,Just In Time Compiler)。本章我们来讨论一下上面提到的第一类编译过程

Javac编译器

我们都直接或间接的使用过Javac编译器,它可以将Java源码文件编译为Class字节码文件。Javac做了许多针对Java语言编码过程的优化措施来提升编码风格和编码效率。Java中很多新的语法特性,并非直接靠虚拟机底层直接支持,而是通过编译器的语法糖来实现

Javac的编译过程大致分为3个过程:解析与填充符号表、插入式注解处理器的注解处理过程、分析与字节码生成

解析与填充符号表

(1)解析

解析包含词法分析语法分析两个过程

词法分析是将源代码中的字符流转变为Token集合的过程。Token是编程过程中的最小元素,比如“int a = b + 1”这行代码一共包含了6个Token,分别是int、a、=、b、+、1

语法分析是根据Token序列构造抽象语法树(Abstract Syntax Tree,AST)的过程。抽象语法树是一种描述程序语法结构的树形表示形式。后续的操作都是建立在抽象语法树之上

(2)填充符号表

完成了解析之后,下一步就是填充符号表。符号表是一组符号地址和符号信息构成的表格。符号表中所记录的信息在编译的不同阶段都要用到。在语义分析中,符号表将用于语义检查和产生中间代码。在目标代码生成阶段,符号表将是对符号名进行地址分配的依据

注解处理器

在JDK1.5之后,Java语言提供了对注解的支持。在JDK1.6中,提供了一组插入式注解处理器的API。这组API可以在编译期读取、修改、添加抽象语法树中的任意元素。在此期间,如果抽象语法树被修改,编译器将重新回到解析与填充符号表的过程重新处理,直到抽象语法树没有再被修改为止。这组API的意义就在于,通过它可以以编程的方式干涉编译器的行为

语义分析与字节码生成

语法分析的结果,使编译器获得了抽象语法树。抽象语法树能够表示一个结构正确的程序抽象,但却无法保证程序符合逻辑。语义分析的作用就是结合上下文,对程序的逻辑进行审查

(1)标注检查

标注检查涉及的内容如:变量使用前是否被声明、给变量赋值的数据类型是否匹配等。其中有一个重要的动作称为常量折叠,比如有如下代码:

int a = 1 + 2;

经过常量折叠,与之等效的代码如下:

int a = 3;

也就是说,对于类似上述两段代码,在运行时效率是相同的。原因在于编译期已经进行过常量折叠

(2)数据及控制流分析

数据及控制流分析涉及的内容如:局部变量使用前是否被赋值、方法的每条路径是否都有返回值、是否所有的Check Exception都被正确处理等。编译期的数据及控制流分析与类加载(关于类类加载方面的内容,请参考本系列文章:类加载机制)时的数据及控制流分析的目的基本是一致的,但是对于特定的校验项只能在编译期或者加载时期进行

比如下面这两个方法,区别在于方法参数及方法体内局部变量是否被final修饰:

但是在编译后,两个方法却是一样的,final修饰符被去除:

原因在于:类变量(实例变量、静态变量)在常量池中有CONSTANT_Field_info符号引用(关于类文件结构方面的内容,请参考本系列文章:类文件结构),而局部变量没有,自然也就没有访问标志信息(access_flags),因此也就不会有变量不变性的信息。也就是说,变量不变性仅在编译期保证

(3)解语法糖

语法糖,是一种方便程序员使用,但是对功能没有影响的语法。Java中许多新的特性并非通过修改虚拟机底层来支持,而是通过语法糖来实现。比如:泛型、变长参数、自动装箱、拆箱等。这些特性在编译阶段被还原成简单的基础语法结构,这个过程就叫做解语法糖。关于语法糖的内容,后面再做介绍

(4)字节码生成

字节码生成是Javac编译过程的最后一个阶段,但这个阶段并不仅仅是把前面各步骤生成的信息转化为字节码并写入磁盘,编译器在此阶段还进行了少量的代码添加和转换工作

比如:实例构造器<init>()方法和类实例构造器<cinit>()方法就是在这个阶段被添加到语法树中的(这里的实例构造器并不是默认构造方法,如果程序中没有提供任何构造方法,那么编译器会在填充符号表阶段添加一个默认构造方法)。这两个构造器会将调用父类的实例构造器代码、变量的初始化(实例变量和类变量)代码、语句块(“{}”块和“static {}”块)代码按此顺序进行收敛

除此之外,还会进行一些代码的优化工作,比如将字符串的加操作替换为StringBuilder.append()等。之后生成最终的Class文件,至此整个编译过程完成

比如源码如下:

编译后:

语法糖

前面提到过,语法糖,是一种方便程序员使用,但是对功能没有影响的语法。Java中许多新的特性并非通过修改虚拟机来做底层支持,而是通过语法糖来实现。下面来举例说明:

泛型

在一些编程语言中(比如C#),泛型在源码以及编译后都是切实存在的,List<int>与List<String>就是两个不同的类型,这种称为真实泛型

但是在Java中,泛型仅存在于源码,经过编译后,泛型会被擦除(被替换为原生类型,并且在相应的地方插入了强制类型转换),这种称为伪泛型

比如源码如下:

编译后通过javap查看其反汇编代码:

红色框中创建map,但是并没有泛型信息,说明泛型被擦除

蓝色框中调用map.put()方法,参数类型都是Object,并没有泛型信息,说明泛型被擦除

绿色框中调用map.get()方法,参数及返回类型都是Object,并没有泛型信息,说明泛型被擦除。checkcast指令检查是否可进行类型转换

黄色框中,通过LocalVariableTypeTable、Signature属性记录原始的泛型信息,但这并不等于泛型信息没有被擦除(关于LocalVariableTypeTable和Signature的信息,可查看Java Virtual Machine Specification)。所谓擦除,仅仅是对方法Code属性中的字节码进行擦除,但是在元数据中依然保留了泛型信息

自动装箱、拆箱、可变参数、foreach循环

比如源码如下:

编译后通过javap查看其反汇编代码:

红色框中创建一个长度为3的Integer类型数组,用于Arrays.asList(T... t)方法的参数,说明可变参数实际是通过数组来实现的

蓝色框中分别将3个int类型参数转换为Integer,用于存入上一步创建的Integer类型数组,这就是自动装箱

绿色框中通过迭代器对list进行遍历,说明foreach循环是通过迭代器来实现的。这也是为什么foreach循环遍历的对象要求实现Iterable接口

黄色框中将每次遍历的Integer类型数据转换为int类型,这就是自动拆箱

除了上面介绍的这些,Java还有不少其他语法糖,如内部类、枚举、断言、针对枚举和字符串的switch语句、try语句中定义和关闭资源等。这些均可通过javap命令了解其本质

思维导图:

笔记6结束

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,013评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,205评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,370评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,168评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,153评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,954评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,271评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,916评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,382评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,877评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,989评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,624评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,209评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,199评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,418评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,401评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,700评论 2 345

推荐阅读更多精彩内容