深入浅出JVM之执行引擎的解释执行与编译执行

news2024/9/24 7:25:19

本篇文章围绕执行引擎,深入浅出的解析执行引擎中解释器与编译器的解释执行和编译执行、执行引擎的执行方式、逃逸分析带来的栈上分配、锁消除、标量替换等优化以及即时编译器编译对热点代码的探测

执行引擎

hotspot执行引擎结构图

执行引擎分为解释器、JIT即时编译器以及垃圾收集器

执行引擎通过解释器/即时编译器将字节码指令解释/编译为对应OS上的的机器指令

本篇文章主要围绕解释器与即时编译器,垃圾收集器将在后续文章解析

解释执行与编译执行

Java虚拟机执行引擎在执行Java代码时,会有两种选择:解释执行和编译执行

解释执行:通过字节码解释器把字节码解析为机器语言执行

编译执行:通过即时编译器产生本地代码执行

Class文件中的代码到底是解释执行还是编译执行只有Java虚拟机自己才能判断准确

编译过程

经典编译原理: 1.对源码进行词法,语法分析处理 2.把源码转换为抽象语法树

javac编译器完成了对源码进行词法,语法分析处理为抽象语法树,再遍历抽象语法树生成线性字节码指令流的过程

剩下的指令流有两种方式执行

  1. 由虚拟机内部的字节码解释器去将字节码指令进行逐行解释 (解释执行)
  2. 或优化器(即时编译器)优化代码最后生成目标代码 (编译执行)

执行引擎流程图

解释器与编译器

解释器

作用: 对字节码指令逐行解释

优点: 程序启动,解释器立即解释执行

缺点: 低效

即时编译器 (just in time compiler)

Java中的"编译期"不确定

  • 可能说的是执行javac指令时的前端编译器 (.java->.class)
  • 也可能是后端编译器JIT (字节指令->机器指令)
  • 还可能是AOT编译器(静态提前编译器) (.java->机器指令)

作用: 将方法编译成机器码缓存到方法区,每次调用该方法执行编译后的机器码

优点: 即时编译器把代码编译成本地机器码,执行效率高,高效

缺点: 程序启动时,需要先编译再执行

执行引擎执行方式

执行引擎执行方式大致分为3种

-Xint: 完全采用解释器执行

-Xcomp: 优先采用即时编译器执行,解释器是后备选择

-Xmixed: 采用解释器 + 即时编译器

hotspot中有两种JIT即时编译器

Client模式下的C1编译器:简单优化,耗时短(C1优化策略:方法内联,去虚拟化,冗余消除)

Server模式下的C2编译器:深度优化,耗时长 (C2主要是逃逸分析的优化:标量替换,锁消除,栈上分配)

分层编译策略:程序解释执行(不开启逃逸分析)可以触发C1编译,开启逃逸分析可以触发C2编译

解释器,C1,C2编译器同时工作,热点代码可能被编译多次

解释器在程序刚刚开始的时候解释执行,不需要承担监控的开销

C1有着更快的编译速度,能为C2编译优化争取更多时间

C2用高复杂度算法,编译优化程度很高的代码

逃逸分析带来的优化

当对象作用域只在某个方法时,不会被外界调用到,那么这个对象就不会发生逃逸

开启逃逸分析后,会分析对象是否发生逃逸,当不能发生逃逸时会进行栈上分配、锁消除、标量替换等优化

栈上分配内存

 //-Xms1G -Xmx1G -XX:+PrintGCDetails 
 public class StackMemory {
     public static void main(String[] args) {
         long start = System.currentTimeMillis();
 ​
         for (int i = 0; i < 10000000; i++) {
             memory();
         }
 ​
         System.out.println("花费时间:"+(System.currentTimeMillis()-start)+"ms");
 ​
         try {
             TimeUnit.SECONDS.sleep(1000);
         } catch (InterruptedException e) {
             e.printStackTrace();
         }
     }
 ​
     private static void memory(){
         StackMemory memory = new StackMemory();
     }
 }
复制代码

-XX:-DoEscapeAnalysis 花费时间:63ms (未开启逃逸分析)

-XX:+DoEscapeAnalysis 花费时间:4ms (开启逃逸分析)

默认开启逃逸分析

锁消除

同步加锁会带来开销

锁消除:当加锁对象只作用某个方法时,JIT编译器借助逃逸分析判断使用的锁对象是不是只能被一个线程访问,如果是这种情况下就不需要同步,可以取消这部分代码的同步,提高并发性能

标量替换

标量: 无法再分解的数据 (基本数据类型)

聚合量: 还可以再分解的数据 (对象)

标量替换: JIT借助逃逸分析,该对象不发生逃逸,只作用于某个方法会把该对象(聚合量)拆成若干个成员变量(标量)来代替

默认开启标量替换

 public class ScalarSubstitution {
     static class Man{
         int age;
         int id;
 ​
         public Man() {
         }
     }
 ​
     public static void createInstance(){
         Man man = new Man();
         man.id = 123;
         man.age = 321;
     }
     public static void main(String[] args) {
         long start = System.currentTimeMillis();
 ​
         for (int i = 0; i < 10000000; i++) {
             createInstance();
         }
 ​
         System.out.println("花费时间:"+(System.currentTimeMillis()-start)+"ms");
 ​
         try {
             TimeUnit.SECONDS.sleep(1000);
         } catch (InterruptedException e) {
             e.printStackTrace();
         }
     }
 }
复制代码
 //-Xmx200m -Xms200m -XX:+PrintGCDetails 
 //-XX:+DoEscapeAnalysis 设置开启逃逸分析
 //-XX:-EliminateAllocations 设置不开启标量替换 
 //开启逃逸分析 + 关闭标量替换 : 花费时间:93ms
 //开启逃逸分析 + 开启标量替换  : 花费时间:6ms
复制代码

热点代码与热点探测

JIT编译器并不是编译所有的字节码,JIT编译器只编译热点代码

热点代码: 被多次调用的方法 或 方法中多次循环的循环体

栈上替换(OSR): JIT将方法中的热点代码编译为本地机器指令(被多次执行的循环体)

编译对象都是方法,如果是栈上替换则"入口"在方法的循环体开始那里

热点探测功能决定了被调用多少次的方法能成为热点代码

hotspot采用基于计数器的热点探测

  • 方法调用计数器 : 统计方法调用次数
  • 回边计数器 : 统计循环体执行循环次数

方法调用时先判断是否有执行编译后的机器码,有则直接使用方法区的Code cache中的机器码;没有机器码则判断计数器次数是否超过阈值,超过则触发编译,编译后机器码存储在方法区Code cache中使用;最后都没有就使用解释执行

总结

本篇文章将围绕执行引擎,深入浅出的解析执行引擎中的解释器、即时编译器各自执行的优缺点以及原理

执行引擎由解释器、即时编译器、垃圾收集器构成,默认情况下使用解释器与编译器的混合方式执行

即时编译器分为C1、C2编译器,其中C1编译快但优化小,C2开启逃逸分析使用栈上分配、锁消除、标量替换进行优化,编译耗时但是优化大

即时编译器并不是所有代码都编译,而是使用方法技术和循环计数来将热点代码编译成机器码存放在方法区的Code Cache中

在混合执行的模式下,解释器、C1、C2编译器同时工作,分层编译

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

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

相关文章

大数据框架Hadoop篇之Hadoop入门

1. 写在前面 今天开始&#xff0c;想开启大数据框架学习的一个新系列&#xff0c;之前在学校的时候就会大数据相关技术很是好奇&#xff0c;但苦于没有实践场景&#xff0c;对这些东西并没有什么体会&#xff0c;到公司之后&#xff0c;我越发觉得大数据的相关知识很重要&…

Jmeter(二十二):硬件性能监控指标

硬件性能监控指标 一、性能监控初步介绍 性能测试的主要目标 1.在当前的服务器配置情况&#xff0c;最大的用户数 2.平均响应时间ART&#xff0c;找出时间较长的业务 3.每秒事务数TPS&#xff0c;服务器的处理能力 性能测试涉及的内容 1.客户端性能测试&#xff1a;web前…

洛谷——【入门2】分支结构

文章目录题单简介【深基1-2】小学数学 N 合一题目描述问题 1问题 2问题 3问题 4问题 5问题 6问题 7问题 8问题 9问题 10问题 11问题 12问题 13问题 14输入格式输出格式样例 #1样例输入 #1样例输出 #1提示AC代码【深基2.习6】Apples Prologue / 苹果和虫子题目描述输入格式输出格…

尝鲜:SpreadJS-en已出 16.0 SpreadJS-cn 16.0-23年1月出

此次版本更新将带来众多的增强功能&#xff0c;而其中新的文件结构尤为重要&#xff0c;是近几个版本中最重要的架构级更新&#xff01; 其设计目标是 *减少 SSJSON 的体积&#xff0c;平均减小到原来 30% *提供按需加载&#xff08;Lazyload&#xff09;能力&#xff0c;相对…

【学习】https://gitee.com/DingJiaxiong

【学习】https://gitee.com/DingJiaxiong 文章目录【学习】<https://gitee.com/DingJiaxiong>0 前言1 Java SE2 Java Web3 Maven基础4 Git5 SSM框架6 MybatisPlus7 SpringBoot0 前言 事情是这样&#xff0c;我准备把之前所有的笔记都放到Gitee 上了 不用GitHub … 就别问…

Spring的AOP切面应用对【后台对接口增强】

目录&#x1f4da;简介&#xff1a;&#x1f4a8;切面表达式&#xff1a;&#x1f4ad;切面通知类型&#xff1a;&#x1f5fa;️创建项目演示&#xff1a;&#x1f3a2;创建项目&#xff1a;&#x1f383;添加依赖&#xff1a;&#x1f4a8;编写切面类&#xff1a;&#x1f68…

前端显示分页详解

我们在浏览页面的时候&#xff0c;是不是经常看到网页经常是以页面的形式给我们展现出来的&#xff0c;我们以淘宝的页面为例&#xff08;如下图&#xff09;&#xff0c;那这样按照页面呈现有什么好处呢&#xff1f;这篇文章让我们来深入探究了解这其中的奥秘。 优点&#xff…

泛型自动装箱

目录 前言 泛型 1.泛型的目的 2.泛型存在的意义和注意事项&#xff1a; 3.擦除机制 4.泛型的边界 5.泛型方法&#xff1a; 包装类&#xff1a; 前言 只要知道《》是啥意思&#xff0c;其他了解即可 泛型的上界 通配符简单知道就行 泛型 1.泛型的目的 指定当前的容器&am…

【JVM】浅析程序计数器与虚拟机栈

文章目录1. 程序计数器2. 虚拟机栈3. 栈内存溢出1. 程序计数器 Program Counter Register 程序计数器&#xff08;寄存器&#xff09; 程序计数器的作用是什么&#xff1f; 是记录下一条JVM指令的执行地址行号 程序计数器有什么特点&#xff1f; 线程私有的不会存在内存溢出 …

ADI Blackfin DSP处理器-BF533的开发详解59:DSP控制ADXL345三轴加速度传感器的应用2(含源码)

硬件准备 ADSP-EDU-BF533&#xff1a;BF533开发板 AD-HP530ICE&#xff1a;ADI DSP仿真器 软件准备 Visual DSP软件 硬件链接 MEMS三轴加速度传感器 我做了一个三轴加速度传感器的子卡&#xff0c;插在这个板子上&#xff0c;然后写了一些有意思的应用程序。 硬件实现原理…

Linux——定制Linux

Linux启动流程 首先&#xff0c;Linux要通过自检&#xff0c;检查硬件设备有没有故障如果有多块启动盘的话&#xff0c;需要在BIOS选择启动磁盘启动MBR中的bootloader引导程序加载内核文件执行所有进程的父进程、老祖宗systemd欢迎界面 加载内核文件的关键文件 kernel文件&a…

C#调用Python脚本训练并生成AI模型(以Paddle框架为例)

目录一. IronPython语言移植1.1 IronPython安装1.2 示例代码1.3 运行结果1.4 特点二. C#调用Python文件打包dll2.1 步骤2.2 限制三. C#命令行调用.py文件执行3.1 代码3.3 运行结果3.4 特点四. C#调用Python可执行exe4.1 步骤4.1.1 使用pyinstaller打包python程序4.1.2 在c#中调…

入门:手动构建镜像

前面我们已经了解了Docker镜像的结构&#xff0c;实际上所有常用的应用程序都有对应的镜像&#xff0c;我们只需要下载这些镜像然后就可以使用了&#xff0c;而不需要自己去手动安装&#xff0c;顶多需要进行一些特别的配置。当然要是遇到某些冷门的应用&#xff0c;可能没有提…

【细胞分割】中值滤波+分水岭法细胞计数【含Matlab源码 640期】

⛄一、图像分割简介 理论知识参考&#xff1a;【基础教程】基于matlab图像处理图像分割【含Matlab源码 191期】 ⛄二、部分源代码 clear; close all; %------------------ %程序中定义图像变量说明 %Image->原图变量; %Image_BW->二值化图象; %Image_BW_medfilt->中…

【实时数仓】DWM层跳出明细计算之需求分析、读取数据、通过Flink的CEP完成跳出判断、写回kafka、测试

文章目录一 DWM层-跳出明细计算1 需求分析与思路&#xff08;1&#xff09;什么是跳出&#xff08;2&#xff09;计算跳出行为的思路&#xff08;3&#xff09;实现思路2 读取数据&#xff08;1&#xff09;代码编写&#xff08;2&#xff09;测试3 通过Flink的CEP完成跳出判断…

【MATLAB100个实用小技巧】——数值分析(85-100)

文章目录前言系列文章85.86. 三次样条插值法87. NEWTON 插值88. hermite 插值89. newton 形式的 hermite 插值90. 平方根法91. gauss 消去法92. 三角分解法93. jacobi 迭代法94. gauss 迭代法95. sor 迭代法96. 最速下降法97. 共额梯度法98. newton 迭代法99. broyden 迭代法10…

前端媒体查询@media示例详解和calc()函数的使用

媒体查询media media 可以针对不同的屏幕尺寸设置不同的样式&#xff0c;特别是如果需要设置设计响应式的页面&#xff0c;media 是非常有用的。当重置浏览器大小的过程中&#xff0c;页面也会根据浏览器的宽度和高度重新渲染页面。 eg&#xff1a;如果文档宽度小于 500 像素…

pytorch 自编码器实现图像的降噪

自编码器 自动编码器是一种无监督的深度学习算法&#xff0c;它学习输入数据的编码表示&#xff0c;然后重新构造与输出相同的输入。它由编码器和解码器两个网络组成。编码器将高维输入压缩成低维潜在(也称为潜在代码或编码空间) &#xff0c;以从中提取最相关的信息&#xff…

SpringCloud之Hystrix

复杂分布式体系结构中的应用程序有数十个依赖关系&#xff0c;每个依赖关系在某些时候将不可避免失败&#xff01; 服务雪崩 多个微服务之间调用的时候&#xff0c;假设微服务A调用微服务B和微服务C&#xff0c;微服务B和微服务C又调用其他的微服务&#xff0c;这就是所谓的“…

HttpRunner3.x 安装与使用

HttpRunner3.x 安装与使用HttpRunner3.x 安装与使用安装使用运行脚手架项目方式一&#xff1a;录制生成用例步骤1&#xff1a;导出har文件步骤2&#xff1a;转化成测试用例文件har2casehmake步骤3&#xff1a;执行测试用例方式二&#xff1a;手工编写测试用例HttpRunner3.x 安装…