1、概述

Java程序最初是通过解释器(Interpreter)进行解释执行的,当虚拟机发现某个方法或代码块的运行特别频繁时,就会把这些代码认定为“热点代码”(Hot Spot Code)。

为了提高热点代码的执行效率,在运行时,虚拟机将会把这些代码编译成与本地平台相关的机器码,并进行各种层次的优化,完成这个任务的编译器称为即时编译器(Just In Time Compiler,下文中简称JIT编译器)。

2 HotSpot虚拟机内的即时编译器

2.1、解释器和编译器

解释器的优势:快速启动和执行,节约内存。

编译器的优势:更高的执行效率。

HotSpot虚拟机中内置了两个即时编译器,分别称为Client Compiler和Server Compiler,或者简称为C1编译器和C2编译器。

用户可以使用参数“-Xint”强制虚拟机运行于“解释模式”(Interpreted Mode) ,也可以使用参数“-Xcomp”强制虚拟机运行于“编译模式”(Compiled Mode)

2.2、分层编译

分层编译根据编译器编译、 优化的规模与耗时,划分出不同的编译层次,其中包括:

第0层,程序解释执行,解释器不开启性能监控功能(Profiling),可触发第1层编译。

第1层,也称为C1编译,将字节码编译为本地代码,进行简单、 可靠的优化,如有必要将加入性能监控的逻辑。

第2层(或2层以上),也称为C2编译,也是将字节码编译为本地代码,但是会启用一些编译耗时较长的优化,甚至会根据性能监控信息进行一些不可靠的激进优化。

解释器还可以作为编译器激进优化时的一个“逃生门”

2.3、编译对象与触发条件

在运行过程中会被即时编译器编译的“热点代码”有两类,即:

被多次调用的方法。

被多次执行的循环体  --  仍然会编译整个方法

热点探测方法:

基于采样的热点探测(Sample Based Hot Spot Detection) :采用这种方法的虚拟机会周期性地检查各个线程的栈顶,如果发现某个(或某些)方法经常出现在栈顶,那这个方法就是“热点方法”。 容易受干扰。

基于计数器的热点探测(Counter Based Hot Spot Detection): 麻烦,但精确。

HotSpot虚拟机中使用的是第二种——基于计数器的热点探测方法 ,因此它为每个方法准备了两类计数器:方法调用计数器(Invocation Counter)和回边计数器(Back Edge Counter)。 当计数器超过阈值溢出了,就会触发JIT编译 。

方法调用计数器

当超过一定的时间限度,如果方法的调用次数仍然不足以让它提交给即时编译器编译,那这个方法的调用计数器就会被减少一半,

这个过程称为方法调用计数器热度的衰减(Counter Decay),

而这段时间就称为此方法统计的半衰周期(Counter Half Life Time)。

回边计数器:对代码段执行次数计数

它的作用是统计一个方法中循环体代码执行的次数[2],在字节码中遇到控制流向后跳转的指令称为“回边”(Back Edge)

回边计数器没有计数热度衰减的过程,因此这个计数器统计的就是该方法循环执行的绝对次数。

2.4、编译过程

这里就会用到很多优化手段,其中Client侧用的少些,Server侧用的多些。

优化方法后面讲,

还可以通过参数配置查看优化过程,但需要Debug版本的虚拟机。

3、编译优化技术

3.1 优化技术概览

3.2、方法内联(Method Inlining)

方法内联的重要性要高于其他优化措施,它的主要目的有两个,

一是去除方法调用的成本(如建立栈帧等),

二是为其他优化建立良好的基础,方法内联膨胀之后可以便于在更大范围上采取后续的优化手段,从而获取更好的优化效果。

因此,各种编译器一般都会把内联优化放在优化序列的最靠前位置。

方法内联的难点:虚方法。因为虚方法在编译时是无法确定指向的,而java又大量存在虚方法。

所以在许多情况下虚拟机进行的内联都是一种激进优化。

3.3、公共子表达式消除(Common Subexpression Elimination)

原理很简单,int d=(c * b)*12+a+(a+b * c);此表达式中,b*c即被公共子表达式。

3.4、数组边界检查消除(Array Bounds Checking Elimination)

解决问题:数组每次访问都去判断下标是否越界太耗性能。

思路:

1、把运行期检查提前到编译期完成,通过语义,断定不会异常的就不检查。

2、隐式异常处理,直接访问,然后捕获segment_fault,转化抛出该抛的异常。

3.5、逃逸分析(Escape Analysis)

并不是直接优化代码的手段,而是为其他优化手段提供依据的分析技术。

逃逸分析的基本行为就是分析对象动态作用域:

当一个对象在方法中被定义后,它可能被外部方法所引用,例如作为调用参数传递到其他方法中,称为方法逃逸。

甚至还有可能被外部线程访问到,譬如赋值给类变量或可以在其他线程中访问的实例变量,称为线程逃逸。

对未逃逸对象的优化方法:

1、栈上分配(Stack Allocation) ,可极大减小内存回收的压力。  -- HotSpot中暂时还没有做这项优化。

2、同步消除(Synchronization Elimination) :由于对象不会被其他线程使用,所以不需要同步锁了。

3、标量替换(Scalar Replacement) :把局部对象拆成多个局部标量,除了可以用栈存标量外,还可以做进一步优化。

标量(Scalar)是指一个数据已经无法再分解成更小的数据来表示了,Java虚拟机中的原始数据类型(int、 long等数值类型以及reference类型等)都不能再进一步分解,它们就可以称为标量。

相对的,如果一个数据可以继续分解,那它就称作聚合量(Aggregate),Java中的对象就是最典型的聚合量。

如果把一个Java对象拆散,根据程序访问的情况,将其使用到的成员变量恢复原始类型来访问就叫做标量替换。

逃逸分析的缺点:

不能保证逃逸分析的性能收益必定高于它的消耗。

如果要完全准确地判断一个对象是否会逃逸,需要进行数据流敏感的一系列复杂分析,从而确定程序各个分支执行时对此对象的影响。

这是一个相对高耗时的过程,如果分析完后发现没有几个不逃逸的对象,那这些运行期耗用的时间就白白浪费了,所以目前虚拟机只能采用不那么准确,但时间压力相对较小的算法来完成逃逸分析。

还有一点是,基于逃逸分析的一些优化手段,如上面提到的“栈上分配”,由于HotSpot虚拟机目前的实现方式导致栈上分配实现起来比较复杂,因此在HotSpot中暂时还没有做这项优化。

4、Java与C/C++的编译器对比

Java与C/C++的编译器对比实际上代表了最经典的即时编译器与静态编译器的对比。

JAVA的劣势:

1、即时编译占运行时间。

2、动态类型安全检查耗费时间。

3、虚方法的使用频率远远大于C++,多态选择的频率也就大。

4、动态扩展使得全局优化难以进行。

5、堆内存回收

Java的劣势虽多,但劣势都是为了换取开发效率上的优势而付出的代价。

Java的优势:

1、即时编译器可以在运行时根据动态状态做优化。如调用频率等。

理解java虚拟机工作后了解吗,【深入理解JAVA虚拟机】第4部分.程序编译与代码优化.2.运行期优化。这章提到的具体的优化技术,应该对以后做性能工作会有帮助。...相关推荐

  1. [深入理解Java虚拟机]第十一章 程序编译与代码优化-晚期(运行期)优化

    概述 在部分的商用虚拟机(Sun HotSpot.IBM J9)中,Java程序最初是通过解释器(Interpreter)进行解释执行的,当虚拟机发现某个方法或代码块的运行特别频繁时,就会把这些代码认 ...

  2. 深入理解Java虚拟机——程序编译与代码优化 (转)

    2019独角兽企业重金招聘Python工程师标准>>> 深入理解Java虚拟机--程序编译与代码优化 (转) 博客分类: java 一早期(编译期)优化 1概述 Java语言的&qu ...

  3. 深入理解Java虚拟机-程序编译与代码优化

    本博客主要参考周志明老师的<深入理解Java虚拟机>第二版 读书是一种跟大神的交流.阅读<深入理解Java虚拟机>受益匪浅,对Java虚拟机有初步的认识.这里写博客主要出于以下 ...

  4. 深入理解JVM(程序编译与代码优化篇)

    程序编译与代码优化 前言 Javac编译器 语义分析与字节码生成 标注检查 数据及控制流分析 解语法糖 字节码生成 后端编译与优化 及时编译器 编译对象和触发条件 编译过程 编译器优化技术 方法内联 ...

  5. 理解java虚拟机工作后了解吗_JAVA入门到再次入门——深入理解JAVA虚拟机(二)|七日打卡...

    前言 为什么叫做入门到到再次入门请参考前一篇或个人博客,在此不再赘述,嗯哼,了解了JVM的基本运行流程以及内存结构,算是初步认识了JVM,跟着课本往前走,继续了解根据JVM的内存模型探索java当中变 ...

  6. mysql种编译码写在哪_深入理解Java虚拟机(程序编译与代码优化)

    对于性能和效率的追求一直是程序开发中永恒不变的宗旨,除了我们自己在编码过程中要充分考虑代码的性能和效率,虚拟机在编译阶段也会对代码进行优化.本文就从虚拟机层面来看看虚拟机对我们所编写的代码采用了哪些优 ...

  7. java虚拟机编译顺序_深入理解Java虚拟机(程序编译与代码优化)

    文章首发于微信公众号:BaronTalk,欢迎关注! 对于性能和效率的追求一直是程序开发中永恒不变的宗旨,除了我们自己在编码过程中要充分考虑代码的性能和效率,虚拟机在编译阶段也会对代码进行优化.本文就 ...

  8. 非计算机专业学java好找工作吗_非计算机专业学习java容易找工作吗?

    原标题:非计算机专业学习java容易找工作吗? 两年前,我还是一名普普通通的在校大学生.上大学的时候除了学习专业知识以外,我还自己在校园里摆摊,买一些生活用品和文具.那个时候同学都夸我,说我有经商头脑 ...

  9. Linux虚拟机克隆后的ip设置(原虚拟机网卡设置为静态ip)

    一.虚拟机克隆后开机前生成MAC地址 打开虚拟机设置>>网络适配器>>高级>>生成>>确定 二.修改物理网卡信息 1.打开虚拟机 2.切换root身份v ...

最新文章

  1. iperf3与netperf使用
  2. java如何学习javaweb学习课程
  3. switch手柄可以连电脑吗_电脑可以拍照吗
  4. Android Gesture 手势识别使用实例 - Android - mobile - ITeye论坛
  5. sonar 设置模板(Template)
  6. day7 java的封装
  7. 两台linux电脑怎么互推文件夹,Llinux文件目录权限及chmod命令简析
  8. mybatis plug 只查id_MyBatis Generator的一个问题引发对插件的修改
  9. Python抓取国家统计局地址数据
  10. 备战秋招之数电知识查漏补缺
  11. 【Opencv卸载与重装】NVIDIA Xavier NX下,卸载opencv3,重装opencv4
  12. html如何加页脚,html-如何将页脚扩展到页面底部?
  13. archlinux音量管理
  14. 转:技术牛人画技术配图的经验分享
  15. 给定一个10位的整数组成的串,形式如: d1d2d3d4d5d6d7d8d9d10 。 其中最后的一位(即 )是校验和,其使用以下运算规则以前面的9位上的整数作为参数获得结果
  16. R语言作图好看的秘籍:RColorBrewer调色板详解
  17. 【黄啊码】php商城搭建从0到n,可用于毕业设计
  18. linux内核4.14.10查看网卡型号,Linux中查看硬件信息命令
  19. maven settings.xml文件
  20. AcWing P378 骑士放置 题解

热门文章

  1. hdu5185 dp:和为n且满足后一项是前一项或者+1的数列个数
  2. super返回不过来
  3. [物理学与PDEs]第1章第7节 媒质中的 Maxwell 方程组 7.2 媒质交界面上的条件
  4. cocos2d-x android 移植 问题
  5. 祝我亲爱的天蝎GG生日快乐!+相识3周年小纪念
  6. 在公共区块链中通过加密保护数据
  7. GitHub为所有人免费提供了所有核心功能-这就是您应该关心的原因
  8. node.js中模块_在Node.js中需要模块:您需要知道的一切
  9. OpenStack环境搭建(三:Computer与Controller节点的安装及配置)
  10. 从基于网络的安装服务器安装操作系统,PXE 概述 - Sun Fire X4800 服务器安装指南(适用于 Linux 操作系统)...