目录
一、编译器概述
二、编译器组成
三、示例
四、深入理解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. jQuery插件制作

    模板:(function($){ $.fn.plugins=function(options){ var defaults = { } var options = $.extend(defaults, ...

  2. Eclipse 安装反编译插件

    前言:在实际的开发中几乎都会使用到一些框架来辅助项目的开发工作,对于一些框架的代码我们总怀有一些好奇之心,想一探究竟,有源码当然更好了,对于有些JAR包中的代码我们就需要利用反编译工具来看一下了,下面 ...

  3. Java 代码安全(一)      —— 避免用String储存敏感数据

    Java 代码安全(一)      -- 避免用String储存敏感数据 如果重要的数据(保存在内存中)在使用后没有及时清理,有可能会导致信息泄漏.开发人员通常都回用String 保存敏感数据(密码, ...

  4. onmouseover事件

    根据教学视频写了个onmouseover事件: <!DOCTYPE html> <html> <head> <meta charset="UTF-8 ...

  5. Java泛型知识点:泛型类、泛型接口和泛型方法

    有许多原因促成了泛型的出现,而最引人注意的一个原因,就是为了创建容器类. 泛型类 容器类应该算得上最具重用性的类库之一.先来看一个没有泛型的情况下的容器类如何定义: public class Cont ...

  6. Html5-audio标签简介及手机端不自动播放问题

    1.audio:html5音频标签 <audio loop src="/photo/aa.mp3" id="audio" autoplay preload ...

  7. OK 开始实践书上的项目一:即使标记

    OK 开始实践书上的项目一:及时标记 然而....又得往前面看啦! ----------------------我是分割线------------------------ 代码改变世界

  8. Linux Bootup Time

    Linux Bootup Time 英文原文地址:http://elinux.org/Boot_Time 1.   简介 启动时间这一话题包括很多子话题,比如启动时间的衡量.启动时间的分析.人为因素分 ...

  9. JQuery简单动画效果的发生顺序和animate方法

    (1)在同一组元素上的效果 当在一个.animate()方法中以多个属性的方式应用时,是同时发生的. 当以方法连缀的形式应用时,是按顺序发生的(排队效果)---除非queue选项值为false. (2 ...

  10. 【算法系列学习】codeforces D. Mike and distribution 二维贪心

    http://codeforces.com/contest/798/problem/D http://blog.csdn.net/yasola/article/details/70477816 对于二 ...