深入理解JVM(程序编译与代码优化篇)
程序编译与代码优化
- 前言
- Javac编译器
- 语义分析与字节码生成
- 标注检查
- 数据及控制流分析
- 解语法糖
- 字节码生成
- 后端编译与优化
- 及时编译器
- 编译对象和触发条件
- 编译过程
- 编译器优化技术
- 方法内联
- 逃逸分析
- 公共子表达式消除
- 数组边界检查消除
- 后记
前言
Java中的编译主要分为3个部分:
- 前段编译:把.java文件转化为.class文件的过程。
- 即时编译:把字节码转化为本地机器码的过程。
- 提前编译:把字节码转化为与目标及其指令集相关的二进制代码的过程。
如果单指对代码运行效率的“优化”,那么在前端编译的Javac中几乎没有任何优化措施可言,虚拟机中全部的优化措施都集中在运行期的即时编译中,这样可以让不是Javac产生的Class文件同样得到性能优化。
但是如果指开发阶段的“优化”,Javac也有许多优化措施能够降低程序员的编码复杂度、提高编码效率。比如说许多Java的新特性都是通过语法糖来实现的。
Javac编译器
从Javac的代码总体结构看,前端编译大致可以分为1个准备阶段和3个处理阶段:
- 准备过程:初始化插入式注解器。
- 解析与填充符号表过程,包括:
- 词法语法分析。将源代码的字符流转变为标记集合,构造出抽象语法树。
- 填充 符号表。产生符号地址和符号信息。
- 插入式注解处理器的处理过程。
- 分析与字节码生成,包括:
- 标注检查。对语法静态信息的检查。
- 数据流及控制流分析。对程序动态运行信息的检查。
- 解语法糖。
- 生成字节码。
在上述过程中注解处理过程中总会生成新的符号,所以又必须回到之前的解析过程。
语义分析与字节码生成
我们编码时候能够在IDE中看到出错的代码被标注了红线,其绝大部分都是来源于语义分析阶段的检查结果。语义分析过程主要可以分为标注检查和数据及控制流分析两个步骤。
标注检查
主要检查诸如变量使用前是否声明、变量与赋值之间的数据类型是否能够匹配等等。
在标注检查中还会进行一个常量折叠的优化操作,例如:int a = 1 + 2
,经过常量折叠之后,1+2会被直接优化成3,这也是为数不多的前端优化之一。
数据及控制流分析
这一部分主要是对代码流程的分析,比如局部变量在赋值之前是否被声明、方法的每条路径是否具有返回值、是否所有的受常异常都被正确处理了之类。
解语法糖
语法糖是指在计算机中添加某种语法,这种语法对编译过程和结果没有任何影响,但是能够方便程序员使用该语言。
在Java中比较常用的语法糖包括:泛型、变长参数、自动装箱拆箱 等。
解语法糖就是指在前端编译的过程中把语法糖进行还原。
字节码生成
字节码生成阶段不仅仅是把前面各个步骤生成的信息转成字节码指令存放到磁盘上,编译器还进行了少量的代码添加和转换工作。
例如添加实例构造器和类构造器。
后端编译与优化
后端编译指的是把Class文件转换成与本地基础设施相关的二进制机器码。以下内容,指的是在HotSpot虚拟机中的编译器。
及时编译器
在HotSpot中,Java程序最开始都是使用解释器解释执行的,在运行一段时间之后,虚拟机会发现某些方法或者代码块的运行非常频繁,这部分代码被称为“热点代码”,为了提高热点代码的运行效率,虚拟机将会把这些代码编译成本地机器代码,并且使用各种手段进行优化,完成这个任务的编译器被称为即时编译器。
解释器除了在最开始的时候能够帮助程序快速运行之外,在后面的过程中还可以帮助编译器收集代码运行的各种信息,也可以在编译器激进优化出错的时候充当逃生门。
在HotSpot虚拟机中采用了两个(或三个)即时编译器,其中前两个存在已久,被称为“客户端编译器”(C1)和“服务端编译器”(C2),第三个编译器是Graal编译器,目标是为了取代C2编译器,目前还在试验阶段。
编译对象和触发条件
前面提到运行过程中即时编译器的目标是“热点代码”,这里的热点代码主要包括两类:
- 多次被调用的方法
- 多次执行的循环体、
那么虚拟机如何判断热点代码呢?主要有两个方法:
- 基于采样的热点探测:定期的检查各个线程的调用栈顶,如果发现某个方法经常出现在栈顶,那么该方法就是热点代码。
- 基于计数器的热点探测:为每个方法建立计数器,统计方法的执行次数,一旦超过某个阈值,那么该代码就是热点代码。
在HotSpot中采用的主要是第二种方法,虚拟机为每个方法准备了两类计数器:方法调用计数器和回边计数器。
方法调用计数器的次数并非是绝对的,当超过一定时间限度,次数仍然不足以提交给即时编译器,那么该方法的调用计数器将会减少一半,这个过程被称为方法调用计数器的热度衰减。而回边计数器的调用次数则是绝对的。
编译过程
那么编译器是如何进行编译的呢?
对于客户端编译器来说,主要分为三个阶段。
第一个阶段会将字节码转换成一种高级中间代码表示(HIR),在这个阶段也会对字节码进行方法内联、常量传播等优化。
第二个阶段将会把HIR转变成低级中间代码表示(LIR),这一步将在HIR中完成空值检查消除、范围检查消除等优化。
最后将在LIR上分配寄存器,并在LIR上做窥孔优化,最后产生机器码。
而服务端编译器则更加复杂,会进行大部分优化动作,还可以根据客户端编译器和解释器提供的信息做出一些激进优化。
编译器优化技术
由于优化技术多种多样,这边只介绍其中比较具有代表性的4个:
- 最重要的优化技术之一:方法内联
- 最前沿的优化技术之一:逃逸分析
- 语言无关的经典优化技术之一:公共子表达式消除
- 语言相关的经典优化技术之一:数组边界检查消除。
方法内联
方法内联说白了就是把调用方法的代码“复制”到调用的地方。
但是在Java中实现也并非是这么简单,虚拟机会首先进行类型继承关系分析,如果不是虚函数,则直接进行内联就行了,但是如果是虚函数,则需要查看是否有多个版本,如果有只有一个版本则进行守护内联,但是如果有多个版本,则会进行内联缓存,在第一次调用的时候缓存下方法和版本号,如果下一次版本号一致,则使用缓存中的方法。
逃逸分析
逃逸分析的原理就是指对对象作用域的分析,根据对象作用域的不同进行不同的优化操作。
- 栈上分配:如果一个对象没有逃逸出线程,那么可以直接在栈上分配对象,对象将随着栈帧出栈而消亡。
- 标量替换:如果一个对象没有逃逸出方法,可以不创建这个对象,而是把对象拆分为各个成员变量。
- 同步消除:如果一个对象没有逃出线程,也就不会被其他线程访问,那么关于这个对象的同步措施可以消除掉。
公共子表达式消除
如果一个表达式E之前已经被计算过了,并且E中的值没有发生任何变化,那么E就被称为公共子表达式,程序将不会再次计算表达式E,而是采用之前的计算结果。
数组边界检查消除
Java中访问数组元素的时候会自动进行上下界的安全检查,但是如果一个数组下标识常量,那么在编译期根据数据流分析的结果来确定常量是否越界就行了,不需要每次都进行越界检查。
后记
《深入理解Java虚拟机》的学习笔记到此告一段落。
昨天和上一届的师兄聊天,师兄表示面试的时候提问到并发和虚拟机的部分其实并不是很多,大部分内容都集中在Java基础和算法的部分,所以接下来几篇都会针对Java基础部分来写,根据内推军面经提纲中的知识点进行逐一学习。
深入理解JVM(程序编译与代码优化篇)相关推荐
- 深入理解Java虚拟机——程序编译与代码优化 (转)
2019独角兽企业重金招聘Python工程师标准>>> 深入理解Java虚拟机--程序编译与代码优化 (转) 博客分类: java 一早期(编译期)优化 1概述 Java语言的&qu ...
- 深入理解Java虚拟机-程序编译与代码优化
本博客主要参考周志明老师的<深入理解Java虚拟机>第二版 读书是一种跟大神的交流.阅读<深入理解Java虚拟机>受益匪浅,对Java虚拟机有初步的认识.这里写博客主要出于以下 ...
- mysql种编译码写在哪_深入理解Java虚拟机(程序编译与代码优化)
对于性能和效率的追求一直是程序开发中永恒不变的宗旨,除了我们自己在编码过程中要充分考虑代码的性能和效率,虚拟机在编译阶段也会对代码进行优化.本文就从虚拟机层面来看看虚拟机对我们所编写的代码采用了哪些优 ...
- java虚拟机编译顺序_深入理解Java虚拟机(程序编译与代码优化)
文章首发于微信公众号:BaronTalk,欢迎关注! 对于性能和效率的追求一直是程序开发中永恒不变的宗旨,除了我们自己在编码过程中要充分考虑代码的性能和效率,虚拟机在编译阶段也会对代码进行优化.本文就 ...
- 深入理解JVM(一)编译openJDK
此文总结的很不错:https://www.cnblogs.com/ACFLOOD/p/5528035.html 准备openJDK源码和环境 1.在linux和macOS上编译openJDK更加友好, ...
- 程序编译与代码优化 -- 早期(编译期)优化
1. 概述 Java编译器可能是指一个前端编译器(其实叫"编译器的前端"更准备一些),把*.java文件转变成*.class文件的过程:也可能是指虚拟机的后端运行期编译器(JIT编 ...
- [深入理解Java虚拟机]第十一章 程序编译与代码优化-晚期(运行期)优化
概述 在部分的商用虚拟机(Sun HotSpot.IBM J9)中,Java程序最初是通过解释器(Interpreter)进行解释执行的,当虚拟机发现某个方法或代码块的运行特别频繁时,就会把这些代码认 ...
- 理解java虚拟机工作后了解吗,【深入理解JAVA虚拟机】第4部分.程序编译与代码优化.2.运行期优化。这章提到的具体的优化技术,应该对以后做性能工作会有帮助。...
1.概述 Java程序最初是通过解释器(Interpreter)进行解释执行的,当虚拟机发现某个方法或代码块的运行特别频繁时,就会把这些代码认定为"热点代码"(Hot Spot C ...
- 小师妹学JVM之:深入理解JIT和编译优化-你看不懂系列
文章目录 简介 JIT编译器 Tiered Compilation分层编译 OSR(On-Stack Replacement) Deoptimization 常见的编译优化举例 Inlining内联 ...
最新文章
- python四大软件-PYPL 9月编程语言排行榜发布 Python一枝独秀
- ·记2014.5.16的一件小事
- 基于数据挖掘的旅游推荐APP(二):主界面布局
- linux vi 命令大全
- Python random() 函数
- 安装和运行Maven
- 充电桩前端对接的一点总结
- 升级核心产品,不和用户竞争,UCloud进军产业互联网差异化路线
- cms php上传失败,PHPCMSV9无法上传附件,提示空信息的超级bug
- MapX从数据库读取数据形成新图层【转载】
- excel密码破解软件Excel Password Unlocker下载和使用技巧(亲测有效!)
- SPSS典型相关分析(Canonical Correlation Analysis)案例(SPSS25最新版)
- C语言复健(数组) 珠心算测验
- 计算机主机内最核心部件,计算机硬件系统中最核心的部件是什么
- linux基本知识点汇集
- 双十一特辑:Python采集商品数据,实时了解商品价格
- P和NP以及NPC、NP-Hard问题
- Leecode第九天,广度优先搜索之矩阵,腐烂的橘子
- 《流浪地球》反响强烈,车联网现状又该如何发展?
- 敬畏传奇——直面第一台可编程电子计算机:Colossus
热门文章
- angular使用@angular/material 出现export 'ɵɵinject' was not found in '@angular/core'
- 招商信诺完成15例新冠病毒感染肺炎赔付;东呈减免湖北加盟酒店重大费用 | 美通企业日报...
- Problem N: 设计飞机类Plane及其派生类
- STM32F103RCT6Mini开发板搭建指纹识别系统
- 509实验室打印机双面打印的方法
- 计算机网络: IP地址,子网掩码,默认网关,DNS服务器详解
- Chromedriver各个版本浏览器驱动下载地址
- mysql 校对规则作用_讲讲Mysql中的校对规则究竟是怎么一回事
- windows基于TCP/IP的简单文件/图片传输
- 微信公众号 去除 “防欺诈盗号请勿支付或输入qq密码“ 提示