009 早期(编译器)优化

javac 编译的过程大致可以分为3个过程,分别是:

  • 解析与填充符号表过程。
  • 插入式注解处理器的注解处理过程。
  • 分析与字节码生成过程。

这3个步骤之间的关系与交互顺序如图

1、解析与填充符号表

①词法、语法分析

词法分析是将源代码的字符流转变为标记(Token)集合,单个字符是程序编写过程的最小元素,而标记则是编译过程的最小元素,关键字、变量名、字面量、运算符都可以成为标记,在Javac的源码中,词法分析过程由com.sun.tools.javac.parser.Scanner类来实现。

语法分析是根据Token序列构造抽象语法树的过程,抽象语法树(Abstract Syntax Tree,AST)是一种用来描述程序代码语法结构的树形表示方式,语法树的每一个节点都代表着程序代码中的一个语法结构(Construct),例如包、类型、修饰符、运算符、接口、返回值甚至代码注释等都可以是一个语法结构。

②符号表填充

符号表(Symbol Table)是由一组符号地址和符号信息构成的表格,读者可以把它想象成哈希表中K-V值对的形式(实际上符号表不一定是哈希表实现,可以是有序符号表、树状符号表、栈结构符号表等)。符号表中所登记的信息在编译的不同阶段都要用到。在语义分析中,符号表所登记的内容将用于语义检查(如检查一个名字的使用和原先的说明是否一致)和产生中间代码。在目标代码生成阶段,当对符号名进行地址分配时,符号表是地址分配的依据。

2、注解处理器

插入式注解处理器可以读取、修改、添加抽象语法树中的任意元素。如果这些插件在处理注解期间对语法树进行了修改,编译器将回到解析及填充符号表的过程重新处理,直到所有插入式注解处理器都没有再对语法树进行修改为止,每一次循环称为一个Round

3、语义分析与字节码生成

语法分析之后,编译器获得了程序代码的抽象语法树表示,语法树能表示一个结构正确的源程序的抽象,但无法保证源程序是符合逻辑的。而语义分析的主要任务是对结构上正确的源程序进行上下文有关性质的审查,如进行类型审查。

语义分析过程分为标注检查以及数据及控制流分析两个步骤。

①标注检查

标注检查步骤检查的内容包括诸如变量使用前是否已被声明、变量与赋值之间的数据类型是否能够匹配等。

②数据及控制流分析

数据及控制流分析是对程序上下文逻辑更进一步的验证,它可以检查出诸如程序局部变量在使用前是否有赋值、方法的每条路径是否都有返回值、是否所有的受查异常都被正确处理了等问题。编译时期的数据及控制流分析与类加载时的数据及控制流分析的目的基本上是一致的,但校验范围有所区别,有一些校验项只有在编译期或运行期才能进行。

将局部变量声明为final,对运行期是没有影响的,变量的不变性仅仅由编译器在编译期间保障。

③解语法糖

语法糖(Syntactic Sugar),也称糖衣语法,指在计算机语言中添加的某种语法,这种语法对语言的功能并没有影响,但是更方便程序员使用。通常来说,使用语法糖能够增加程序的可读性,从而减少程序代码出错的机会。

④字节码生成

来自为知笔记(Wiz)

时间: 2024-11-05 11:25:20

009 早期(编译器)优化的相关文章

C语言 之编译器优化

C语言的编译器会对变量和代码进行一定的优化,我们看下面这个例子. int a,b,c; a=1; b=a; c=b; 这个程序正常运行的时候会这样子:先把a指向的内存空间内放入1 再把a指向的内存空间里的数读出来放到b指向的内存空间, 最后再把b指向的内存空间里的数读出来放到c指向的内存空间里. 然而编译器要对这个程序进行一定的优化,编译的时候,直接把1放入三个内存空间中. 这样子在正常运行的时候确实是好事,但是总会有一些特殊的情况发生,比如,一个中断程序突然改变了a的值,那么会发生这样的情况:

编译器优化陷阱——全局指针多次使用异常

做程序开发一定会和编译器打交道,编译器优化可以给我们代码运行带来一定的提升,但也可能存在一些意想不到的问题.下面就是我在开发时候遇到的一个坑,希望可以给大家一些借鉴 直接上代码说话吧 1 static unsigned char* s_data = NULL; //存储一帧视频数据 2 void DoRendering () 3 { 4 // D3D11 case 5 if (s_DeviceType == kUnityGfxRendererD3D11 && EnsureD3D11Reso

C#编译器优化那点事

使用C#编写程序,给最终用户的程序,是需要使用release配置的,而release配置和debug配置,有一个关键区别,就是release的编译器优化默认是启用的. 优化代码开关即optimize开关,和debug开关一起,有以下几种组合. | 编译器开关设置 |C#IL代码质量 | JIT本地代码质量 | | ------------- |:-------------:| -----:| | /optimize- /debug-(默认) | 未优化 | 有优化 | | /optimize-

对String类型的认识以及编译器优化

Java中String不是基本类型,但是有些时候和基本类型差不多,如String b = "tao" ; 可以对变量直接赋值,而不用 new 一个对象(当然也可以用 new). Java中的变量和基本类型的值存放于栈内存,而new出来的对象本身存放于堆内存,指向对象的引用还是存放在栈内存.例如如下的代码: int  i=1; String s =  new  String( "Hello World" ); 变量i和s以及1存放在栈内存,而s指向的对象"H

深入理解JVM读书笔记四: (早期)编译器优化

10.1概述 Java 语言的 "编译期" 其实是一段 "不确定" 的操作过程,因为它可能是指一个前端编译器(其实叫 "编译器的前端" 更准确一些)把 .java 文件转变成 .class 文件的过程:也可能是指虚拟机的后端运行期编译器(JIT 编译器,Just In Time Compiler)把字节码转变成机器码的过程:还可能是指使用静态提前编译器(AOT 编译器,Ahead Of Time Compiler)直接把 *.java 文件编译成

一个函数返回临时对象引起的编译器优化问题

我们都知道,如果在一个函数调用另一个函数,假设是 main 函数调用 fun 函数,这个 fun 函数返回一个临时类类型变量,那么这个时候编译器就会在 main 函数申请一个空间并生成一个临时对象,通过拷贝构造函数将 fun 返回的临时变量的值拷贝到这个临时对象.我们看如下的代码: #include <iostream> #include <cstring> using namespace std; class Matrix { public: explicit Matrix(do

编译器,优化,及目标代码生成.

本文介绍从源文件开始到目标代码生成的过程. 首先,是我们每天都要接触的源文件.源文件是由纯ASCII或者其他字符集组成的文本,由程序员使用文本编辑器创建.它有以下的几种形式 纯文本.好处是易于维护.并且可以使用处理文本文件的程序来处理源文件. 这个就是我们最常见的源代码形式了.甚至可以使用notepad来处理源文件! 记号化的源文件.使用专门的单字节"记号"值来表示源文件中的保留字等语句元素. 好处1:尺寸小,由于使用单字节的符号来"压缩"多字符的保留字,所以比纯文

[Inside HotSpot] C1编译器优化:条件表达式消除

1. 条件传送指令 日常编程中有很多根据某个条件对变量赋不同值这样的模式,比如: int cmov(int num) { int result = 10; if(num<10){ result = 1; }else{ result = 0; } return result; } 如果不进行编译优化会产出cmp-jump组合,即根据cmp比较的结果进行跳转.可以使用gcc -O0查看: cmov(int): push rbp mov rbp, rsp mov DWORD PTR [rbp-20],

[Inside HotSpot] C1编译器优化:全局值编号(GVN)

1. 值编号 我们知道C1内部使用的是一种图结构的HIR,它由基本块构成一个图,然后每个基本块里面是SSA形式的指令,关于这点如可以参考[Inside HotSpot] C1编译器工作流程及中间表示.值编号(Value numbering)是指为每个计算得到的值分配一个独一无二的编号,然后遍历指令寻找可优化的机会.比如下面的代码: a = 1;b=4; c = a+b; d = a+b; e = b; 编译器可以在计算a的时候为它指定一个hash值(0x12a3e)然后放入hash表:b同理指定