作者:郭嘉

邮箱:allenwells@163.com

博客:http://blog.csdn.net/allenwells

github:https://github.com/AllenWell

为什么会Java虚拟机会同一时候存在解释器和编译器呢?

这是为了兼顾启动效率和运行效率两个方面。Java程序最初是通过解释器进行解释运行的,当虚拟机返现某个方法或代码块的运行特别频繁时,就会把这段代码标记为热点代码,为了提供热点代码的运行效率,在运行时,虚拟机就会把这些代码编译成与本地平台相关的机器码。并进行各种层次的优化。

当编译器做的激进优化不成立,不如载入了新类后类型继承结构出现变化。出现了罕见陷阱时能够进行逆优化退回到解释状态继续运行。

以上描写叙述的两种配合关系例如以下图所看到的:

一 编译模式

HotSpot JVM内置了两个编译器,各自是Client Complier和Server Complier,虚拟机默认是Client模式。我们也能够通过

  • -client:强制虚拟机运行Client模式
  • -server:强制虚拟机运行Server模式

而不管是Client模式还是Server模式,虚拟机都会运行在解释器和编译器配合使用的混合模式下。能够通过

  • -Xint:强制虚拟机运行于解释模式
  • -Xcomp:强制虚拟机运行于编译模式

以上描写叙述的运行模式例如以下图所看到的:

二 分层编译

为什么会存在分层编译?

这是由于编译器编译本机代码须要占用程序运行时间,要编译出优化程度更高的代码锁花费的时间可能更长,并且想要编译出优化程度更高的代码,解释器可能还要替编译器收集性能监控信息。这对解释运行的速度也有影响。为了在程序启动响应速度和运行效率之间寻找平衡点。因此採用分层编译的策略。

分层策略例如以下所看到的:

  • 第0层:程序解释运行。解释器不开启性能监控功能,可触发第1层编译。
  • 第1层:即C1编译。将字节码编译为本地代码。进行简单和可靠的优化,如有必要将增加性能监控的逻辑。
  • 第2层:即C2编译,将字节码编译为本地代码,同一时候启用一些编译耗时较长的优化,甚至会依据性能监控信息进行一些不可靠的激进优化。

三 编译对象

编译对象即为会被编译优化的热点代码。有下面两类:

  • 被多次调用的方法
  • 被多次运行的循环体

四 触发条件

上面描写叙述中使用多次这个概念,那么什么算多次呢?

这就牵扯到触发条件这个概念,推断一段代码是否是热点代码。是否须要触发即时编译,这样的行为成为热点探測(Spot Dectection)。

热点探測有两种手段:

4.1 基于採样的热点探測(Sample Based Hot Spot Dectection)

虚拟机会周期性的检查各个线程的栈顶,假设发现某些方法常常性的出如今栈顶,那么这种方法就是热点方法。

4.2 基于计数器的热点探測(Counter Based Hot Spot Dectection)

虚拟机会为每一个方法或代码块建立计数器,统计方法的运行次数。假设运行次数超过一定的阈值就觉得他是热点方法。

HotSpot JVM使用另外一种方法基于计数器的热点探測方法。它为每一个方法准备了两类计数器:

4.2.1 方法调用计数器

这个阈值在Client模式下是1500次。在Server模式下是10000此,这个阈值能够通过參数-XX:CompileThreadhold来人为设定。

假设不做不论什么设置。方法调用次数统计的并非方法被调用的绝对次数,而是相对的运行频率,即一段时间内方法被调用的次数,当超过一定时间限度,假设方法的调用次数仍然不足以让它提交给即时编译器编译,那这种方法的调用计数器会被降低一半,这个过程被称为方法调用计数器的热度衰减(Counter Decay)。而这段时间就称为此方法统计的半衰周期(Counter Half Life Time)。相同也能够使用參数-XX:-UseCounterDecay来关闭热度衰减。

方法调用计数器触发即时编译的整个流程例如以下图所看到的:

4.2.2 回边计数器

什么是回边?

在字节码遇到控制流向后跳转的指令称为回边(Back Edge)。

回边计数器是用来统计一个方法中循环体代码运行的次数,回边计数器的阈值能够通过參数-XX:OnStackReplacePercentage来调整。

  • 虚虚拟机运行在Client模式下,回边计数器阂值计算公式为:
方法调用计数器闭值( CompileThreshold) xOSR比率(OnStackReplacePercentage) / 100

当中OnSlackReplacePercentage默认值为933,假设都取默认值.那Client模式虚拟机的回边计数器的阂值为13995.

  • 虚拟机运行在Servo模式下,回边计数器阂值的itm公式为:
方法调用计数器阂值(CompileThmshold) x (OSR比率(OnStackReplacePercentage) - 解释器监控比率(InterpreterProffePercentage) / 100

当中OnStackReplacePementage默认值为140. InterpreterPmfilePercenmgc默认值为33.

假设都取默认值。BF Server模式虚拟机回边计数器的阑值为10700。

回边计数器触发即时编译的流程例如以下图所看到的:

回边计数器与方法调用计数器不同的是,回边计数器没有热度衰减,因此这个计数器统计的就是循环运行的绝对次数。

五 编译流程

在默认设置下,不管是方法调用产生的即时编译请求,还是OSR编译请求,虚拟机在代码编译器还未完毕之前,都仍然依照解释方式继续进行,而编译动作则在后台的编译线程中继续进行。也能够使用-XX:-BackgroundCompilation来禁止后台编译,则此时一旦遇到JIT编译,运行线程向虚拟机提交请求后会一直等待,直到编译完毕后再開始运行编译器输出的本地代码。

那么在后台编译过程中,编译器做了什么事呢?

Server Compiler和Client Compiler的后台编译过程是不一样的,我们来分别看一下。

5.1 Client Compiler编译流程

  1. 第一阶段:一个平台独立的前端将字节码构造成一种高级中间码表示(High Level Infermediate Representaion),HIR使用静态单分配的形式来表示代码值,这能够使得一些的构造过程之中和之后进行的优化动作更easy实现,在此之前编译器会在字节码上完毕一部分基础优化,如方法内联、常量传播等。
  2. 第二阶段:一个平台相关的后端从HIR中产生低级中间代码表示(Low Level Intermediate Representation),而在此之前会在HIR上完毕还有一些优化。如空值检查消除、范围检查消除等。以便让HIR达到更高效的代码表示形式。
  3. 第三阶段:在平台相关的后端使用线性扫描算法(Linear Scan Register Allocation)在LIR上分配寄存器,并在LIR上做窥孔优化(Peephole)优化,然后产生机器码。

整个步骤例如以下图所看到的:

5.1 Server Compiler编译流程

Server Compiler是专门面向服务端的典型应用并为服务器的性能配置特别调整过的编译器,它会运行全部经典的优化动作。例如以下所看到的:

  • 无用代码消除
  • 循环展开
  • 循环表达式外提
  • 消除公共子表达式
  • 常量传播
  • 基本块重排序
  • 范围检查消除
  • 空值检查消除
  • 守护内联
  • 分支频率预測

【Java 虚拟机探索之路系列】:JIT编译器的更多相关文章

  1. Java虚拟机解释器与JIT编译器

    一.JAVA编译相关概念 1.动态编译(dynamic compilation)指的是“在运行时进行编译”:与之相对的是事前编译(ahead-of-time compilation,简称AOT),也叫 ...

  2. 【Java】实战Java虚拟机之五“开启JIT编译”

    今天开始实战Java虚拟机之五“开启JIT编译” 总计有5个系列 实战Java虚拟机之一“堆溢出处理” 实战Java虚拟机之二“虚拟机的工作模式” 实战Java虚拟机之三“G1的新生代GC” 实战Ja ...

  3. 【Java 安全技术探索之路系列:J2SE安全架构】之二:安全管理器

    作者:郭嘉 邮箱:allenwells@163.com 博客:http://blog.csdn.net/allenwells github:https://github.com/AllenWell 一 ...

  4. JVM规范系列第3章:为Java虚拟机编译

    Oracle 的 JDK 包括两部分内容:一部分是将 Java 源代码编译成 Java 虚拟机的指令集的编译器,另一部分是用于Java 虚拟机的运行时环境. 第一部分应该说的是 Javac 这个前置编 ...

  5. 【深入Java虚拟机】之七:Javac编译与JIT编译

    转载请注明出处:http://blog.csdn.net/ns_code/article/details/18009455 编译过程 不论是物理机还是虚拟机,大部分的程序代码从开始编译到最终转化成物理 ...

  6. Java虚拟机 - Javac编译与JIT编译

    [深入Java虚拟机]之七:Javac编译与JIT编译 编译过程 不论是物理机还是虚拟机,大部分的程序代码从开始编译到最终转化成物理机的目标代码或虚拟机能执行的指令集之前,都会按照如下图所示的各个步骤 ...

  7. 实战Java虚拟机之四:提升性能,禁用System.gc() ?

    今天开始实战Java虚拟机之四:"禁用System.gc()". 总计有5个系列 实战Java虚拟机之一“堆溢出处理” 实战Java虚拟机之二“虚拟机的工作模式” 实战Java虚拟 ...

  8. 实战Java虚拟机之三“G1的新生代GC”

    今天开始实战Java虚拟机之三:“G1的新生代GC”. 总计有5个系列 实战Java虚拟机之一“堆溢出处理” 实战Java虚拟机之二“虚拟机的工作模式” 实战Java虚拟机之三“G1的新生代GC” 实 ...

  9. 实战Java虚拟机之二“虚拟机的工作模式”

    今天开始实战Java虚拟机之二:“虚拟机的工作模式”. 总计有5个系列 实战Java虚拟机之一“堆溢出处理” 实战Java虚拟机之二“虚拟机的工作模式” 实战Java虚拟机之三“G1的新生代GC” 实 ...

随机推荐

  1. JavaScript设计模式基础之面向对象的JavaScript(一)

    动态语言类型与鸭子类型 此内容取自JavaScript设计模式与开发实践一书 编程语言按照数据类型大体可以分为2类,一类就是静态类型语言,另一类则是动态类型语言 静态类型语言也可以称之为编译语言,而动 ...

  2. luogu P3393 逃离僵尸岛-搜索剪枝+spfa

    P3393 逃离僵尸岛 题目描述 小a住的国家被僵尸侵略了!小a打算逃离到该国唯一的国际空港逃出这个国家. 该国有N个城市,城市之间有道路相连.一共有M条双向道路.保证没有自环和重边. K个城市已经被 ...

  3. SpringBoot的线程调度

    Spring Boot默认提供了一个ThreadPoolTaskExecutor作为线程调度器,只需要在配置类中使用注解EnableAsync即可开启异步线程调度.在实际要执行的Bean中使用@Asy ...

  4. 条款17:以独立语句将newed对象置入智能指针(Store newed objects in smart pointers in standalone statements)

    NOTE: 1.以独立语句将newed对象存储于智能指针内.如果不这样做,一旦异常被抛出,有可能导致难以察觉的资源泄漏.

  5. docker:安装tomcat

    文章来源:http://www.cnblogs.com/hello-tl/p/8929879.html 0.下载镜像 # docker pull tomcat:8.5 1.复制tomcat配置 先启动 ...

  6. int内部方法释义

    python基本数据类型包括:int.str.list.tuple.dict.bool.set(),一切事物都是对象,对象由类创建 1. bit_length:返回数字占用的二进制最小位数 def b ...

  7. .NET如何进行数据库连接

    在.net中连接sql server数据库,需引入System.Data.SqlClient命名空间: 1,最简单的连接方式: SqlConnection conn = new SqlConnecti ...

  8. WCF部署到IIS的一个浅水滩

    俗话说,浅水淹死牛.昨天下午到今天上午,我就被淹死了一次. 最近在做毕业设计,和一个朋友做,做的是一个APP,我做的是服务器端,因为涉及后台数据更新,所以要有一个后台管理系统,然后还要搭建一个服务给A ...

  9. 可编辑div的createRange()

    1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 // 在元素的指定位 ...

  10. 大数据学习——VMware安装

    ---恢复内容开始--- 一.下载VMware,安装 二.新建虚拟机 1.FIle-->new virtual machine 后面进入硬件资源分配,其中cpu给1个,内存至少给1G,网卡的选择 ...