目录
一、编译器概述
二、编译器组成
三、示例
四、深入理解JVM中的编译器
五、语法糖
六、补充
 
 
一、编译器概述
1、编译器实质
编译器的实质是将一种语言规范转化为另一种语言规范;由人容易理解到机器容易理解。Javac将java文件中的源代码,转化成JVM能够识别的class文件中的字节码;然后JVM再转化为机器可以识别的机器语言。
2、编译器类型
前端编译器:.java->.class;如Sun的javac
JIT编译器:运行期编译器,字节码->机器码;如Hotspot的C1、C2编译器
AOT编译器:静态提前编译器,.java->机器码
3、前端编译器对代码的优化主要在程序编码(帮助程序员改善编码风格、提高编码效率);而JIT主要是性能优化,这样其他语言的代码也可以被优化。
许多Java新的语法特性,是通过语法糖实现,而不需要虚拟机的底层支持。
 
 
二、编译器组成
1、词法分析器:生成token流。词法分析器读取源文件中的每个字符,识别关键字、分隔符等,生成token流;token流中包括:
(1)java关键词:package、import、public、class、int等
(2)自定义单词:包名、类名、变量名、方法名
(3)符号:=、;、+、-、*、/、%、{、}等
2、语法分析器:生成结构化的、可操作的语法树;其实是将token中的单词组成一句句话。有些地方叫做抽象语法树(AST)。
3、语义分析器:生成注解语法树,更接近目标语言的语法规则。语义分析器在语法树基础上做很多处理,包括
1、添加默认的无参构造器(在没有指定任何有参构造器的情况下)【《JVM》中,在语法分析器和注解处理之前,有一步填充符号表,其中的操作包括添加默认的无参构造器】
2、处理注解:JDK1.6以后,开始提供插入式注解处理器的API,可以理解为编译器的插件;如果注解处理器修改了语法树,编译器将循环进行(解析+填充+注解处理);通过注解处理器,程序员可以干预编译过程。
3、标注:检查语义合法性、进行逻辑判断
•检查语法树中的变量类型是否匹配(eg.String s = 1 + 2;//这样"="两端的类型就不匹配)
•检查变量、方法或者类的访问是否合法(eg.一个类无法访问另一个类的private方法)
•变量在使用前是否已经声明、是否初始化
•常量折叠(eg.代码中:String s = "hello" + "world",语义分析后String s = "helloworld")
•推导泛型方法的参数类型
4、数据及控制流分析【编译时分析与类加载时分析,目的类似,但校验范围有一些区别。如局部变量在内存中没有flag,对final的校验只能在编译阶段进行】
•局部变量使用前是否有赋值【《JavaWeb》认为属于上一步,《JVM》认为属于本步】
•变量的确定性赋值(eg.有返回值的方法必须确定有返回值)
•final变量只能赋一次值,在编译的时候再赋值的话会报错
•所有的检查型异常是否抛出或捕获
•所有的语句都要被执行到(return后边的语句就不会被执行到,除了finally块儿)
5、进一步语义分析
•去掉永假代码(eg.if(false))
•变量自动转换(eg.int和Integer)
•去掉语法糖(eg.foreach转化为for循环,assert转化为if,内部类解析成一个与外部类相关联的外部类)
6、最后,将经过上述处理的语法树转化为最后的注解语法树
【个人记忆】除了注解处理、去除语法糖,可以这么分类:
类:添加无参构造器;变量、方法、类的访问是否合法
字段:变量类型是否匹配;final是否多次赋值;常量折叠
方法外部:返回值是否正确;异常是否抛出或捕获
方法内部:变量使用前是否声明并初始化;所有语句都被执行到;去除永假代码
4、代码生成器:生成字节码
(1)生成<init>()和<cinit>()方法:其实是代码收敛,包括{}、static{}、变量初始化、调用父类的实例构造器等。
(2)其他优化:如字符串的加操作替换为StringBuilder或StringBuffer的append()操作。
(3)生成字节码
 
 
三、示例
(1)源代码到token
package compile;

/**
* 词法
*/
public class Cifa {
int a;
int c = a + 1;
}

(2)语法树
package compile;
public class Yufa {
    int a;
    private int c = a + 1;
    public int getC() {
        return c;
    }
    public void setC(int c) {
        this.c = c;
    }
}

 
 
四、深入理解JVM中的编译器
1、《JVM》一书中对编译器的编译过程的划分不太一样,但大体一致。
2、解析与填充符号表
解析:词法分析和语法分析
填充符号表:符号表是一组符号地址和符号信息构成的表格;符号表中信息在编译阶段多次用到。如语义分析时用于语义检查;字节码生成阶段,当对符号名分配地址时,符号表是地址分配的依据。
3、注解处理
4、语义分析
标注检查
数据及控制流分析
解语法糖
5、字节码生成
 
 
五、语法糖
1、语法糖:在计算机语言中添加某种语法,这种语法能使程序员更方便的使用语言开发程序,同时增强程序代码的可读性,避免出错的机会;但是这种语法对语言的功能并没有影响。Java中的泛型,变长参数,自动拆箱/装箱,foreach循环,条件编译,枚举,内部类,断言,对枚举和字符串的switch,try语句中定义和关闭资源(?)等都是语法糖,即虚拟机本身并不支持。
2、泛型:伪泛型,类型擦除
泛型的本质是参数化类型;泛型出现前常用方法是Object类+类型转换;Java泛型的实现是类型擦除,即伪泛型。
例如List<String>和List<Integer>在运行期是一个类,即List;使用时进行类型转换。
泛型在重载方面的尴尬:下面两个方法无法重载。
public void method(List<Integer> list){}
public void method(List<String> list){}
由于类型被擦除,为了区分不同参数化类型,引入了一些其他属性,如Signature等;因此类型擦除只是对方法的Code属性中的字节码进行擦除,元数据仍保留了泛型信息,因此可以通过反射手段取得参数化类型。【这段理解不深入,略】
3、自动拆箱和自动装箱:intValue()和Integer.valueOf()
变长参数:参数是数组
foreach循环:iterator
4、条件编译
C、C++ 的预处理器最初的任务是解决编译时的代码依赖关系(如#include),而在 Java 语言之中并没有使用预处理器,因为 Java 语言天然的编译方式(不一个个编译 Java 文件,而是将所有编译单元的语法树顶级结点输入到待处理列表后再进行编译,因此各个文件之间能够互相提供符号信息) 无需使用预处理器。
实现举例:
public static void main(String[] args) {
    if(true){
        System.out.println("True");
    }else{
        System.out.println("False");
    }
}
编译后:
public static void main(String args[])
{
    System.out.println("True");
}


注意:只有if+常量可以实现条件编译,常量+while等编译会报错;消除无法到达的分支也是在解语法糖阶段完成;只能实现语句块级别的条件编译。
 
 
六、补充
1、实战:插入式注解处理器,略
2、访问者设计模式:将数据结构(如语法树)和对数据结构的操作解耦;略。
3、主要参考:《深入理解Java虚拟机》
 

JVM-1.编译的更多相关文章

  1. [四] java虚拟机JVM编译器编译代码简介 字节码指令实例 代码到底编译成了什么形式

      前言简介   前文已经对虚拟机进行过了简单的介绍,并且也对class文件结构,以及字节码指令进行了详尽的说明 想要了解JVM的运行机制,以及如何优化你的代码,你还需要了解一下,java编译器到底是 ...

  2. 深入理解JVM(一)编译openJDK

    此文总结的很不错:https://www.cnblogs.com/ACFLOOD/p/5528035.html 准备openJDK源码和环境 1.在linux和macOS上编译openJDK更加友好, ...

  3. JVM笔记——编译期的优化

    一.编译过程 解析和填充符号表的过程 插入注解处理器的注解处理过程 语义分析与字节码生成过程 二.解析和填充符号表 解析包含两个过程:词法分析和语法分析 (一)词法分析 将源代码的字符流转变成标记(T ...

  4. JVM即时编译(JIT)

    Java解释执行过程: 代码装入-代码校验-代码执行 Java字节码的执行方式分为两种:即使编译方式和解释执行方式.即时编译是值解释器先将字节码编译成机器码,然后执行该机器码.解释执行的方式是指解释器 ...

  5. JVM学习——编译OpenJDK

    最近在学习<深入理解java虚拟机 第二版>这本书.书中第一部分建议大家自己编译OpenJDK.抱着学习态度也来编译个玩一玩.下面进入正题. 1.编译环境介绍 操作系统 CentOS Li ...

  6. jvm——分层编译

    https://www.cnblogs.com/andy-zhou/p/5327288.html 分层编译根据编译器编译.优化的规模与耗时,划分出不同的编译层次: 第0层:程序解释执行,解释器不开启监 ...

  7. java 常量 jvm在编译期能确定常量的 所以相等 但不能确定变量的范围 所以变量相加会产生新的

  8. 走进JVM之一 自己编译openjdk源码

    想要深入了解JVM,就必须了解其实现机制.了解JVM实现的最好方法便是自己动手编译JDK.好了,让我们开始吧! 1.  准备工作 获取OpenJDK源码 本次编译选择的是OpenJDK7u,官方源码包 ...

  9. 🏆【JVM技术专区】「编译技术专题」带你彻底认识Java的编译技术

    前提概要 Java的class字节码并不是机器语言,要想让机器能够执行,还需要把字节码翻译成机器指令.这个过程是Java虚拟机做的,这个过程也叫编译.是更深层次的编译. 在编译原理中,把源代码翻译成机 ...

  10. JVM 的 工作原理,层次结构 以及 GC工作原理

    JVM Java 虚拟机 Java 虚拟机(Java virtual machine,JVM)是运行 Java 程序必不可少的机制.JVM实现了Java语言最重要的特征:即平台无关性.原理:编译后的 ...

随机推荐

  1. 初学Java scirpt(判断、循环语句)

    在编写代码时,我们经常需要为不同的判断结果来执行不同的动作以及需要反复执行同一段代码,这时我们就需要使用判断和循环语句来实现. 1.判断语句(if) 判断语句经常用的有(if......else).( ...

  2. User Browsing Model简介

    搜索引擎的点击日志提供了很多有价值的query-doc相关性信息,但是这些信息是有偏的,因为对于用户没有点击过的doc,我们无法确定其是否真实地被用户浏览过.即日志中记录的展现信息与实际的展现信息之间 ...

  3. 从花式swap引出的pointer aliasing问题

    上次,一个同学问我,你知不知道可以不用引入中间变量就可以实现swap? 我说,我知道,可以用加减法或者异或实现,像是这样 void mySwap(int &x,int &y) { x= ...

  4. 为什么大多数培训机构还停留在只教ssh框架?

    最近听一些朋友说,招聘面试的很多人简历都差不多,大部分人的简历上面都写了熟悉ssh框架,我朋友就在吐槽,为什么这些人简历都差不多,并且都熟悉ssh框架? 后面他说, 可能这些人都是培训机构出来的, 然 ...

  5. C语言学习第四章

    今天学习C语言循环结构,为什么要用循环呢?因为有时候我们对一堆的数字进行重复的处理的时候要重复的编写一些相同或者差不多的代码,让程序显得很臃肿,而且写着也麻烦,如果用循环来写的话能简化很多,出错的话也 ...

  6. PPT素才搜索简谈

    PPT已经是大部分公司工作汇报.培训.年度总结等不可缺少的办公软件.下面介绍一些关于PPT素才搜索的方法. 第一:模板    1.搜索大法--filetype:对应的文档格式 在关键词后添加这样一段文 ...

  7. HttpClient研究学习总结

    Http协议非常的重要,HttpClient相比传统JDK自带的URLConnection,增加了易用性和灵活性(具体区别,日后我们再讨论),它不仅是客户端发送Http请求变得容易,而且也方便了开发人 ...

  8. linux操作系统中对大小端的判断

    static union { char c[4]; unsigned long l; } endian_test = { { 'l', '?', '?', 'b' } }; #define ENDIA ...

  9. hibernate 插入数据时让数据库默认值生效

    用hibernate做数据库插入操作时,在数据库端已经设置了对应列的默认值,但插入的数据一直为null.查找资料发现,原来是hibernate的配置项在作怪. Hibernate允许我们在映射文件里控 ...

  10. iOS开发 - Swift实现检测网络连接状态及网络类型

    一.前言 在移动开发中,检测网络的连接状态尤其检测网络的类型尤为重要.本文将介绍在iOS开发中,如何使用Swift检测网络连接状态及网络类型(移动网络.Wifi). 二.如何实现 Reachabili ...