前两天在leetcode做了算法题,惊讶的发现用java实现的时间复杂度,竟然跻身于C/C++同列,甚至偶尔会超过后两者,虽然知道JVM功不可没,但还是很好奇在VM编译过程中到底发生了什么,翻出《深入理解java虚拟机》一探究竟,算是有所收获,记录如下。

概述

java语言的“编译期”其实是一段“不确定”的操作过程,因为可能是下面三种:

  • 前端编译器
    叫编译器的前端可能更合适,主要是把.java文件转变成.class文件。主要种类有:Sun的Javac、Eclipse JDT的增量式编译器(ECJ)。
  • JIT编译器
    就是可能指虚拟机的后端运行期编译器(JIT编译器:Just In Time Compiler),把「字节码」变成「机器码」,主要有:Hotspot VM的C1、C2编译器。
  • AOT编译器
    上面两个可能关心更多,这个是指用静态提前编译器(AOT编译器:Ahead Of Time Compiler)直接把*.java变成本地机器码的过程。

本文后面所指的编译器和编译期都表示第一种。
关于优化的两点解释:

  1. Javac这类编译器对代码的运行效率几乎没有任何优化措施。只是做了一些针对java语言「编码过程」的“优化”措施来改善程序员的「编码风格和编码效率」(俗称“语法糖”,并没有涉及虚拟机底层的改进)。
  2. 虚拟机设计团队把对性能的优化集中到了后端的即时编译中,这样可以让那些不是有javac产生的Class文件(如JRuby、Groovy等语言的Class)也同样能享受到编译器优化所带来的好处。

也就是说java中即时编译器的在运行期的优化对程序运行更重要,而前端编译器在编译期的优化对于程序编码更重要。注意此处的「编译期」和「运行期」、「程序编码」和「程序运行」的区别。

Javac编译器

Javac不像是Hotspot虚拟机本身是CPP(少量C)写的,本身是java语言编写的程序,这样java程序员就很方便了解它的编译过程了。关于源码环境搭建和调试不做详述,大致说下Javac的编译过程,主要分为3个过程,分别是:

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

Java语法糖

语法糖虽不能带来实质性的功能的改进,但是它们或能提高效率,或能提升语法严谨性,或能减少编码出错机会。java中常见的语法糖如下:

泛型与泛型擦除

java的泛型规则只在程序源码中存在,在编译后就已经替换为原来的原生类型(RawType,裸类型),并且在相应的地方插入了强制转换。

自动装箱、拆箱与遍历循环、变长参数

演示代码如下(以下jdk环境1.8):

 

1

2

3

4

5

6

7

8

9

10

11

 

public class AutoBoxCompile {

public static void main(String[] args) {

List<Integer> list =Arrays.asList(1,2,3,4);

int sum=0;

for (Integer i : list) {

sum+=i;

}

System.out.println(sum);

}

}

用的本地jd-gui.exe工具反编译之后变成:

 

1

2

3

4

5

6

7

8

9

10

11

12

 

public class AutoBoxCompile

{

public static void main(String[] args)

{

List list = Arrays.asList(new Integer[] { Integer.valueOf(1), Integer.valueOf(2), Integer.valueOf(3), Integer.valueOf(4) });

int sum = 0;

for (Integer i : list) {

sum += i.intValue();

}

System.out.println(sum);

}

}

foreach明明是一颗语法糖特性,猜想是工具的问题,找了一个在线反编译工具,得到下列代码,可以发现变长参数、自动装箱和遍历循环的语法糖特性:

条件编译

类似于下面这段if代码,在编译过程中就会被“运行”。

 

1

2

3

4

5

6

7

8

9

 

public class Ifcompiler {

public static void main(String[] args) {

if (true) {

System.out.println(1111);

}else {

System.out.println(2222);

}

}

}

生成的字节码只包括System.out.println(1111);,将上述编译之后产生的class反编译如下图:

只有使用条件为常量的if语句才能有上述效果,否则会被拒绝编译比如while(false){};。
还有很多其他的语法糖,比如内部类、枚举、断言、switch以及try中关闭资源等等,有时间再去尝试,重要的是明白语法糖是怎么回事。

最后

「之所以把从java文件到字节码文件的编译器叫做前段编译器,是因为它只完成了从程序到抽象语法树或者中间字节码的转变,而在此之后还有一组内置于虚拟机内部的“后端编译器”完成了从字节码到本地机器码的过程,即前面提到的即时编译器或JIT编译器,这个编译器的编译速度及结果的优劣,是衡量虚拟机性能的很重要的指标。」
JVM还有很多的东西没有去挖掘,希望这是个开端,能不断的去探索java虚拟机更深处的东西。

from:http://zouzls.github.io/2016/09/06/%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3JVM%E4%B9%8B%E5%89%8D%E7%AB%AF%E7%BC%96%E8%AF%91%E5%99%A8%EF%BC%88%E4%B8%80%EF%BC%89/

深入理解JVM之前端编译器(一)相关推荐

  1. 深入理解JVM之JIT编译器(二)

    上篇是分析了一下前段编译器,主要过程完成从java代码到字节码的转变,它的改进顶多是提高程序的编码速度和效率.本篇尝试探索JIT编译器,它能够完成从字节码到本地机器码的转变,从而真正的影响程序的运行效 ...

  2. 深入理解JVM虚拟机(八):编译器优化

    本博客从编译期源码实现的层次上让我们了解了Java源代码编译为字节码的过程,分析了Java语言中泛型.主动装箱/拆箱.条件编译等多种语法糖的前因后果. 1. 概述 java语言的"编译期&q ...

  3. 《深入理解JVM 第三版》 读书笔记

    2 Java内存区域与内存溢出异常 2.2 运行时数据区域 2.2.1程序计数器 程序计数器占用空间较小,可以看作当前 线程执行字节码的行号.因此是线程独立的. 如果执行的是native方法,则该计数 ...

  4. 深入理解JVM内存模型

    博客园 首页 新随笔 联系 管理 订阅 随笔- 323  文章- 0  评论- 40  深入理解JVM-JVM内存模型 我们知道,计算机CPU和内存的交互是最频繁的,内存是我们的高速缓存区,用户磁盘和 ...

  5. [译]深入理解JVM

    深入理解JVM 原文链接:http://www.cubrid.org/blog/dev-platform/understanding-jvm-internals 每个使用Java的开发者都知道Java ...

  6. 一文带你深入理解JVM内存模型

    一文带你深入理解JVM内存模型 一.JAVA的并发模型 共享内存模型 在共享内存的并发模型里面,线程之间共享程序的公共状态,线程之间通过读写内存中公共状态来进行隐式通信 该内存指的是主内存,实际上是物 ...

  7. 深入理解 JVM Class文件格式(十)

    到此, 所有关于class文件格式的重要内容都已经讲解完了, 不敢说面面俱到, 但是敢说大部分重要的内容都包含在内了.前前后后用了9篇博客来专门讲解class文件结构, 为什么花那么多的时间和精力来介 ...

  8. 深入理解 JVM Class文件格式(九)

    经过前八篇关于class文件的博客, 关于class文件格式的内容也基本上讲完了. 本文是关于class文件格式的最后一篇. 在这篇博客中, 将会讲解关于方法的几个属性. 理解这篇博客的内容, 对于理 ...

  9. 深入理解 JVM Class文件格式(八)

    在本专栏的第一篇文章 深入理解Java虚拟机到底是什么 中, 我们主要讲解了什么是虚拟机, 这篇博客是对JVM的一个概述. 在随后的几篇文章中,一直在讲解class文件格式. 在今天这篇博客中, 将会 ...

最新文章

  1. 94年出生,如今985高校博士生导师!
  2. R语言ggplot2可视化删除所有分面图(facet_wrap可视化的facet结果)的标签实战(Remove facet_wrap labels)
  3. UVA11732 strcmp() Anyone?
  4. 发布 SharePoint Server 2007 Starter Page
  5. BZOJ 3993 Luogu P3324 [SDOI2015]星际战争 (最大流、二分答案)
  6. VTK:vtkCompositePolyDataMapper2用法实战
  7. iOS,Objective-C Runtime
  8. OPenGL实例化绘制、普通绘制说明
  9. Oracle数据库用法汇总
  10. 数据结构与算法笔记(五) 链表的应用
  11. MYSQL的索引类型:PRIMARY, INDEX,UNIQUE,FULLTEXT,SPAIAL 区别与使用场合
  12. 【项目篇】Android团队项目开发之统一代码规范
  13. CPU 和 GPU 的区别
  14. echars 绘制多点连线地图 vue
  15. 昆仑通态屏幕制作(连载5)---基础篇(串口接收,文本与灯显示)
  16. CST2018/2020安装注意事项
  17. C51最小系统板红外遥控控制led灯的亮灭
  18. 亚马逊热销爆款产品货源有哪些?亚马逊无货源怎么做?
  19. b站网页html,bilibili注册页面html简单分析
  20. Linux C/C++编程:netstat分析tcp状态转移(socket通信)

热门文章

  1. Linux 2.4调度系统分析--转
  2. 2013年小结及2014年展望
  3. 主要几种通信协议的性能比较(转载)
  4. 三、【线性表】线性表概述
  5. jvm性能调优实战 -56没有WHERE条件的SQL语句引发的OOM MAT 排查步骤
  6. JVM - 解读GC中的 Safe Point Safe Region
  7. Redis进阶-分布式存储 Sequential partitioning Hash partitioning
  8. ngrok-外网访问内网工具NGROK的使用
  9. 第一篇博客——用来写自己
  10. mysql查询员工排班信息_考勤管理信息系统人员排班管理及查询