目录
一、编译器概述
二、编译器组成
三、示例
四、深入理解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. virtual box ubuntu 与Windows共享文件夹

    由于懒得去截图了,直接抛链接.参考链接:http://www.cnblogs.com/lidabo/p/5317024.html 简介概括:首先安装增强功能,接着在virtual box的seting ...

  2. Linux常用命令快查

    一.读取配置文件中某一个变量的值 假如有一个配置文件dubbo.properties,需要读取dubbo.application.name的值: dubbo.application.name=book ...

  3. C# 调用C++dll出现的问题。

    问题描述: 对 PInvoke 函数“winform应用!winform应用.Form1::add”的调用导致堆栈不对称.原因可能是托管的 PInvoke 签名与非托管的目标签名不匹配.请检查 PIn ...

  4. A GDI+ Based Character LCD Control

    This is a renew. A GDI+ Based Character LCD Control by Conmajia Character liquid crystal display (LC ...

  5. Java Tomcat 启动失败的解决思路

    好久没写新博文了,离自己要求的每年几篇的目标也是渐行渐远. 最近在学习Java,也是从基础学习,因为我是一个.net开发人员,所以学习的成本相对低些.Java JDK,JRE,Tomact 的安装及配 ...

  6. WebStorm设置左侧菜单栏背景和字体设置

    WebStorm左侧菜单栏 webstorm是一款前端IDE利器,个人感觉黑色的背景比较炫酷,刚开始从网上下载的主题只能修改编辑窗口的背景色,经过查询资料终于把左边菜单栏的背景色也修改了. 第一步:点 ...

  7. 【翻译】FreeMarker——入门

    原文传送门 1. Template + data-model = output data-model是一个树状模型,通常是一个java对象. 2.data-model 入门 hashes(散列):目录 ...

  8. phtread_mutex 组合

    phtread_mutex通过mutexattr设定其类型,并保存在成员__kind中.pthread_mutex的锁操作函数根据__kind进行方法的分派(dispatch).__kind由5个字段 ...

  9. JavaScript常用的方法和函数(setAttribute和getAttribute )

    仅记录学习的新知识和示例,无干货. 1.setAttribute和getAttribute          (Attribute:属性) setAttribute:为元素添加指定的属性,并为其赋值: ...

  10. hdu1054 Strategic Game 树形DP

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1054 思路:树形DP,用二分匹配也能解决 定义dp[root][1],表示以root 为根结点的子树且 ...