【JVM技术专题】 深入学习JIT编译器实现机制「 原理篇」

news2024/12/19 17:17:01

前提概要

解释器

Java程序最初是通过解释器(Interpreter)进行解释执行的,当虚拟机发现某个方法或代码块的运行特别频繁的时候,就会把这些代码认定为“热点代码”(hotspot code)。正因为如此,我们的hotspot的虚拟机就是因此而得名。

解释器优点

  • (占用空间较少)解释执行占用更小的内存空间

  • (启动和首次执行速度较快)当程序需要迅速启动的时候,解释器可以首先发挥作用,省去了编译的时间,立即执行

  • (提高动态性和移植性)当处于程序的动态效果下,如果预先编译好所有相关的静态本地代码后,就无法实现动态化扩展,以及提高移植到其他计算机平台架构下的能力


编译器

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

编译器优点

  • (提高运行速度)在程序运行时,随着时间的推移,编译器逐渐发挥作用,把越来越多的代码编译成本地代码之后,可以获得更高的执行效率。

  • (逆转优化)同时,当编译器进行的激进优化失败的时候,还可以进行逆优化来恢复到解释执行的状态。

因此,整个虚拟机执行架构中,解释器与编译器经常配合工作,如下图所示。

解释器与编译器并存的架构(流程)

  1. 如果Java程序需要迅速启动和执行时,或者只是执行一次,解释器可首先发挥作用,省去编译时间,立即执行程序运行后,随着时间推移,JIT编译器逐渐发挥作用,把越来越多的代码编译成本地代码后,可获取更高执行效率。

  2. 程序运行环境中内存资源限制较大(如部分嵌入式系统中),可使用解释执行节约内存,反之可使用JIT编译执行提升效率

  3. 解释器还可作为JIT编译器激进优化时的一个“逃生门”,让编译器根据概率选择一些大多数时候都能提升运行速度的优化手段,当激进优化的假设不成立时可通过逆优化(Deoptimization)退回到解释状态继续执行

故,在整个虚拟机执行架构中解释器与编译器经常配合工作

  • Xint设置:用户可以使用参数 -Xint 强制虚拟机运行于 “解释模式”(Interpreted Mode),这时候编译器完全不介入工作。

  • -Xcomp设置:强制虚拟机运行于 “编译模式”(Compiled Mode),这时候将优先采用编译方式执行,但是解释器仍然要在编译无法进行的情况下接入执行过程

  • -Xmixed设置:这种配合使用的方式称为“混合模式”(Mixed Mode)

通过虚拟机 -version 命令可以查看当前默认的运行模式。

即时编译器(JIT编译器)

JIT编译器不是虚拟机的必需部分,但JIT编译器编译性能的好坏、代码优化程度的高低是衡量一款商用虚拟机优秀与否的最关键的指标之一,也是虚拟机中最核心且最能体现虚拟机技术水平的部分

被编译对象和触发条件

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

编译的目标对象

  • 被多次调用的方法
    • 编译器会将整个方法作为编译对象,这也是标准的JIT 编译方式
  • 被多次执行的循环体
    • 由循环体出发的,但是编译器依然会以整个方法作为编译对象,因为发生在方法执行过程中,称为栈上替换

判断热点代码

「判断一段代码是否是热点代码,是不是需要出发即时编译」这样的行为称为热点探测(Hot Spot Detection),探测算法有两种,分别为

基于采样的热点探测(Sample Based Hot Spot Detection)

虚拟机会周期的对各个线程栈顶进行检查,如果某些方法经常出现在栈顶,这个方法就是“热点方法”

  • 优点:实现简单、高效,很容易获取方法调用关系。

  • 缺点:很难确认方法的reduce(衰减),容易受到线程阻塞或其他外因扰乱


基于计数器的热点探测(Counter Based Hot Spot Detection)

为每个方法(甚至是代码块)建立计数器,执行次数超过阈值就认为是“热点方法”

  • 优点:统计结果精确严谨。

  • 缺点:实现麻烦,不能直接获取方法的调用关系

HotSpot使用的是第二种-基于技术其的热点探测,并且有两类计数器:

  • 方法调用计数器(Invocation Counter )

  • 回边计数器(Back Edge Counter )


两个即时编译器

从上面的解释器和编译器的协同合作架构图中,应该可以了解到,JVM虚拟机实现了两个不同的JIT编译器,分别称为 Client Compiler和 Server Compiler ,或者简称为 C1 编译器和 C2 编译器

热点触发的阈值

这两个计数器都有一个确定的阈值,超过后便会触发JIT编译,具体细节和内容下面会详细讲述。

上面提到了一下两种热点探测的计数器:

方法调用计数器(Invocation Counter )
  • 首先是方法调用计数器:

    • Client模式下默认阈值是1500 次。

    • Server 模式下是 10000次。

    • 这个阈值可以通过 -XX:CompileThreshold 来人为设定。

  • 如果不做任何设置,方法调用计数器统计的并不是方法被调用的绝对次数,而是一个相对的执行频率,即一段时间之内的方法被调用的次数。(可以理解为滑动窗口)。

  • 当超过一定的时间限度,如果方法的调用次数仍然不足以让它提交给即时编译器编译,那么这个方法的调用计数器就会被减少一半,这个过程称为方法调用计数器热度的衰减(Counter Decay),而这段时间就成为此方法的统计的半衰期( Counter Half Life Time)。

  • 进行热度衰减的动作是在虚拟机进行垃圾收集时顺便进行的,可以使用虚拟机参数 -XX:CounterHalfLifeTime 参数设置半衰周期的时间 (时间窗口秒),单位是秒。整个 JIT 编译的交互过程如下图。


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

  • 显然,建立回边计数器统计的目的就是为了触发 OSR 编译。关于这个计数器的阈值, HotSpot 提供了 -XX:BackEdgeThreshold 供用户设置。

但是当前的虚拟机实际上使用了 -XX:OnStackReplacePercentage 来简介调整阈值,计算公式如下:

  • Client模式, 公式为方法调用计数器阈值(CompileThreshold)X OSR 比率(OnStackReplacePercentage)/100 。其中OSR比率默认为933,那么,回边计数器的阈值为13995

  • Server模式,公式为方法调用计数器阈值(Compile Threashold)X (OSR (OnStackReplacePercentage)- 解释器监控比率 (InterpreterProfilePercent))/100

其中onStackReplacePercentage 默认值为 140,InterpreterProfilePercentage 默认值为 33,如果都取默认值,那么 Server 模式虚拟机回边计数器阈值为 10700 。

编译过程

默认情况下,无论是方法调用产生的即时编译请求,还是OSR请求,虚拟机在代码编译器还未完成之前,都仍然将按照解释方式继续执行,而编译动作则在后台的编译线程中进行。

用户可以通过参数 -XX:-BackgroundCompilation来禁止后台编译,这样,一旦达到 JIT 的编译条件,执行线程向虚拟机提交便已请求之后便会一直等待,直到编译过程完成后再开始执行编译器输出的本地代码。

虚拟机运行模式

目前的HotSpot编译器默认的是解释器和其中一个即时编译器配合的方式工作,具体是哪一个编译器,取决于虚拟机运行的模式,HotSpot虚拟机会根据自身版本与计算机的硬件性能自动选择运行模式,用户也可以使用 -client 和 -server 参数强制指定虚拟机运行在 Client 模式或者 Server 模式。

Client Compiler(了解即可) :

它是一个简单快速的三段式编译器,主要关注点在于局部的优化,放弃了许多耗时较长的全局优化手段。

  • 第一阶段,一个平台独立的前端将字节码构造成一种高级中间代码表示(High-Level Intermediate Representaion , HIR)。在此之前,编译器会在字节码上完成一部分基础优化,如 方法内联,常量传播等优化。

  • 第二阶段,一个平台相关的后端从 HIR 中产生低级中间代码表示(Low-Level Intermediate Representation ,LIR),而在此之前会在 HIR 上完成另外一些优化,如空值检查消除,范围检查消除等,让HIR 更为高效。

  • 第三阶段,在平台相关的后端使用线性扫描算法(Linear Scan Register Allocation)在 LIR 上分配寄存器,做窥孔(Peephole)优化,然后产生机器码。

Server Compiler(了解即可):

专门面向服务端典型应用并为服务端性能配置特别调整过的编译器
也是一个充分优化过的高级编译器,几乎能达到GNU C++编译器使用-02参数时的优化强度会执行所有经典的优化动作。

  • 无用代码消除(Dead Code Elimination)、

  • 循环展开(LoopcUnrolling)、

  • 循环表达式外提(Loop Expression Hoisting)、

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

  • 常量传播(Constant Propagation)、

  • 基本块重排序(Basic Block Reordering)等

还会实施一些与Java语言特性密切相关的优化技术,如

  • 范围检查消除(Range Check Elimination)、

  • 空值检查消除(Null Check Elimination)等

还可能根据解释器或Client Compiler提供的性能监控信息,进行一些不稳定的激进优化,如

  • 守护内联(Guarded Inlining)、

  • 分支频率预测(Branch Frequency Prediction)等

  • Server Compiler的寄存器分配器是一个全局图着色分配器,它可充分利用某些处理器架构(如RISC)上的大寄存器集合

编译速度远超传统静态优化编译器,相对Client Compiler代码质量有所提高,可减少本地代码执行时间,从而抵消额外的编译时间开销

如何从外部观察即时编译器的编译过程和编译结果?

  • -XX:+PrintCompilation 在即时编译时,打印被编译成本地代码的方法名称

  • -XX:+PrintInlining 在即时编译时,输出方法内联信息

  • -XX:+PrintAssembly 在即时编译时,打印被编译方法的汇编代码,虚拟机需安装反汇编适配器HSDIS插件,Product版虚拟机需加入参数-XX:+UnlockDiagnosticVMOptions打开虚拟机诊断模式

  • -XX:+PrintOptoAssembly 用于Server VM,输出比较接近最终结果的中间代码表示,不需HSDIS插件支持

  • -XX:+PrintLIR 用于Client VM,输出比较接近最终结果的中间代码表示,不需HSDIS插件支持

  • -XX:+PrintCFGToFile 用于Client Compiler,将编译过程中各阶段数据(如,字节码、HIR生成、LIR生成、寄存器分配过程、本地代码生成等)输出到文件中

  • -XX:PrintIdealGraphFile 用于Server Compiler,将编译过程中各阶段数据(如,字节码、HIR生成、LIR生成、寄存器分配过程、本地代码生成等)输出到文件中

注,要输出CFG或IdealGraph文件,需Debug或FastDebug版虚拟机支持,Product版的虚拟机无法输出这些文件

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1755.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

将matlab数据导入到Python中使用

相信不少小伙伴都遇到过和我一样的问题,就是在尝试使用scipy.io.loadmat将matlab类型的数据导入python中的时候遇到如下错误提示。 import scipy as sc# 设定需要导入的matlab数据目录 path_TE00 r"D:\Window自带文件夹\桌面\PIC Design\Lumerical\Tutorials…

Apollo GraphQL

一、Apollo GraphQL介绍 Apollo 是一个开源的 GraphQL 开发平台, 提供了符合 GraphQL 规范的服务端和客户端实现。使用 Apollo 可以帮助我们更方便快捷的开发使用 GraphQL。 ● 官网:https://www.apollographql.com/ ● GitHub 相关开源仓库&#xff1a…

Vite+Vue+Electron环境搭建

因为electron可以直接加载html文件,也可以直接加载url链接,所以,我们可以在调试过程中使用url地址,来动态显示我们的改变过程。 electron简单来说就是对html的一种封装,所以我们先来搭建vue的开发环境,这里…

LQ0135 左孩子右兄弟【DFS+二叉树】

题目来源:蓝桥杯2021初赛 C A组H题 题目描述 对于一棵多叉树,我们可以通过“左孩子右兄弟” 表示法,将其转化成一棵二叉树。 如果我们认为每个结点的子结点是无序的,那么得到的二叉树可能不唯一。 换句话说,每个结点可…

并发编程之ForkJoin框架

什么是 Fork/Join 框架 Fork/Join 是从 java7 开始提供的并行执行任务的框架,是一个把大任务分割成若干个小任务,最终汇总每个小任务的结果,得到大任务结果的框架. 如下图: Fork/Join 的特性 ForJoinPool 不是为了替代 Execu…

毕业仅1年,干Python赚了50W!网友:不是吹的

前言 惊讶 ​Py现状:Python职位月薪5W起? 其他程序员:心态塌了! 秒杀各行业薪资榜单,拿下编程语言排行榜的Python,工资真的如网上说的开挂了吗?有人在网上发现这样的一条评论信息&#xff1a…

公众号网课查题接口题库

公众号网课查题接口题库 本平台优点: 多题库查题、独立后台、响应速度快、全网平台可查、功能最全! 1.想要给自己的公众号获得查题接口,只需要两步! 2.题库: 题库:题库后台(点击跳转&#xf…

Navicat 现已支持 OceanBase 企业版

近期,PremiumSoft CyberTech Limited 公司发布了 Navicat 16.1.3 版本,正式支持蚂蚁集团旗下的 OceanBase 企业版(MySQL 兼容模式)。此次合作旨在帮助用户通过 Navcicat 进行 OceanBase 企业版的数据库开发及管理,更大…

说说 Redis 事务

Redis 事务简介# Redis 只是提供了简单的事务功能。其本质是一组命令的集合,事务支持一次执行多个命令,在事务执行过程中,会顺序执行队列中的命令,其他客户端提交的命令请求不会插入到本事务执行命令序列中。命令的执行过程是顺序…

MPLS综合实验

目录 实验要求 划分IP地址 首先对MPLSVPN骨干网络进行配置 首先配置IP地址 启动IGP协议 激活MPLS和LDP VRF空间的创建 将接口划入到VRF空间中 R1和R5通过静态路由在CE和PE上配置 建立MP-BGP 对站点R1和R5进行配置 首先把IP给配置好 在VRF空间中发布路由信息 对站点…

2000-2020上市公司全要素生产率LP方法含原始数据和Stata代码

1、时间:2000-2020年 2、指标包括:stkcd、year、证券代码、固定资产净额、营业总收入、营业收入、营业成本、销售费用、管理费用、财务费用、支付给职工以及为职工支付的现金、员工人数、折旧摊销、行业代码、上市日期、AB股交叉码、退市日期、年末是否…

windows下用Java跑通spark官方文档的quick-start

这里写自定义目录标题前置环境官方示例三个小坑maven文件引用不明确未传递master url前置环境 见上一篇:https://blog.csdn.net/shuzip/article/details/115606522 官方示例 https://spark.apache.org/docs/3.1.1/quick-start.html /* SimpleApp.java */ import…

廊坊特色农业 国稻种芯·中国水稻节:河北复合农业促增收

廊坊特色农业 国稻种芯中国水稻节:河北复合农业促增收 新闻中国采编网 中国新闻采编网 谋定研究中国智库网 中国农民丰收节国际贸易促进会 国稻种芯中国水稻节 中国三农智库网-功能性农业农业大健康大会报道:河北廊坊安次区“稻蟹共作”新模式 特色农业…

Codeforces Round #773 (Div. 2)

A. Hard Way 题目链接:Problem - A - Codeforces 样例输入: 5 8 10 10 4 6 2 4 6 0 1 4 2 14 1 11 2 13 2 0 0 4 0 2 4 0 1 1 1 0 0样例输出: 0.0000000 0 2.0000 0.00 1题意:给定一个三角形的三个顶点,输入保证三角…

echarts画各种形状水波图

各种形状水波图 代码 用的是echarts绘制&#xff0c;echarts相关api可以参考echarts官网&#xff0c;形状修改series.shape即可修改形状&#xff0c;这里用的是SVG路径 <html><head><meta charset"utf-8"><link href"https://fonts.google…

jQuery网页开发案例:jQuery常用API--jQuery 尺寸、位置操作及 电梯导航案例和节流阀(互斥锁)

jQuery 尺寸 以上参数为空&#xff0c;则是获取相应值&#xff0c;返回的是数字型。如果参数为数字&#xff0c;则是修改相应值。参数可以不必写单位。这个width方法不包含边框 innerWidth()包含widthpadding 注意这个要大写 outerWidth()包含width padding border outerW…

(JavaSE) 数组

文章目录1. 数组的作用2. 数组的创建及初始化2.1 数组的创建2.2 数组的初始化3. 数组的使用3.1 数组中元素的访问3.2 遍历数组方法4. 数组是引用类型4.1 JVM中的内存有那些4.2 数组如何开辟空间4.3 数组 null 的意思4.4 引用不能同时指向多个对象4.5 数组作为方法返回值5. 二维…

【一键生成】3DMAX配景楼生成插件使用教程

3DMAX室外设计师常常需要创建各种场景配楼&#xff0c;为了解决大场景制作难的情况&#xff0c;3dMax配景楼生成插件是一款傻瓜式的插件或许更能快速让你从繁重的体力劳动中解脱出来&#xff01; 【安装方法】 方法一&#xff1a;拖动插件文件到3dMax窗口。 方法二&#xff1a;…

MySQL 主要线程

文章目录MySQL 主要线程1. Master thread2. io thread3. purge thread4. page Cleaner ThreadMySQL 主要线程 1. Master thread Master thread有四大循环&#xff0c;分别是loop,background loop&#xff0c;suspend loop&#xff0c;flush loop。且四大循环的作用如下: loop…

第二篇 基于自然语言处理的漏洞检测方法综述

杨伊等 来源&#xff1a;计算机研究与发展 目录 1 相关技术 1.1 自然语言处理 1.2 漏洞检测与分析 据2021年CVE漏洞趋势安全报告&#xff0c;当前漏洞类型占比最大的5类漏洞分别是代码执行、拒绝服务、溢出、跨站脚本以及信息获取。基于自然语言处理技术实现漏洞检测的研究…