【JVM详解四】执行引擎
一、概述
Java程序运行时,JVM会加载.class字节码文件,但是字节码并不能直接运行在操作系统之上,而JVM中的执行引擎就是负责将字节码转化为对应平台的机器码让CPU运行的组件。
执行引擎是JVM核心的组成部分之一。可以把JVM架构分成三部分,如下图所示:

执行引擎位于JVM的最下层(图中虚线框部分),可以粗略地看到执行引擎负责和运行时数据区交互。
二、计算机语言的发展史
在讲解执行引擎之前,需要知道什么是机器码、汇编语言、高级语言以及为什么会有Java字节码的出现。
2.1 机器码
各种用 0 和 1 组成的二进制编码方式表示的指令,叫作机器指令码,简称机器码。
计算机发展的初始阶段,人们就用机器码编写程序,我们也称为机器语言。机器语言虽然能够被计算机理解和接受,但由于可读性差不便于人类读写,并且用它编程容易出差错。使用机器码编写的程序一经输入计算机,CPU可以直接读取运行,因此和其他语言编的程序相比,执行速度最快。机器码与CPU紧密相关,所以不同种类的CPU所对应的机器码也就不同。
2.2 汇编语言
由于机器码是由0和1组成的二进制序列,可读性实在太差,于是人们发明了指令。
指令就是把机器码中特定的0和1序列,简化成对应的指令(一般为英文简写,如mov、inc等),可读性稍好,这就是我们常说的汇编语言。在汇编语言中,用助记符 (Mnemonics) 代替机器码的操作码,用地址符号 (Symbol) 或标号 (Label) 代替指令或操作数的地址。
不同的硬件平台,各自支持的指令是有差别的。因此每个平台所支持的指令,称为对应平台的指令集,如常见的x86指令集对应的是x86架构的平台,ARM指令集对应的是ARM架构的平台。不同平台之间指令不可以直接移植。
由于计算机只认识机器码,所以用汇编语言编写的程序还必须经过汇编器转换为机器语言,计算机才能识别和执行。
2.3 高级语言
为了使计算机用户编程序更容易些,后来就出现了各种高级计算机语言。比如C、C++等更容易让人识别的语言。
高级语言则相对汇编语言更易于编写,有更好的可读性,一般编译器会将高级语言转换成汇编语言,然后再由汇编器转换为机器指令由计算机执行。

2.4 字节码
字节码是一种中间状态(中间码)的二进制代码(文件),需要转译后才能成为机器码。Java 程序可以通过编译器将源码编译成 Java 字节码,特定平台上的虚拟机将字节码转译为可以直接执行的指令,也就实现了跨平台性。如下图所示:

三、执行引擎工作过程
- 执行引擎在执行过程中,需要执行什么样的字节码指令取决于PC寄存器(程序计数器);
- 每执行完一项指令操作之后,PC寄存器会更新下一条需要执行的指令地址;
- 方法执行过程中,执行引擎可能会通过局部变量表中的对象引用准确定位到 Java 堆中的对象实例信息,以及通过对象头中的元数据信息定位到目标对象的类型信息。
- 解释器
- JIT编译器
- 垃圾收集器

3.1 解释器
解释器就是一个运行时的“翻译者”,将字节码指令翻译为对应平台的本地机器指令由CPU执行,当一条指令执行完成后再根据PC寄存器中记录的下一条指令执行解释操作。
JVM设计者的初衷是为了满足Java程序实现跨平台特性,因此避免采用静态编译的方式直接生成机器码,从而诞生了实现解释器在运行时采用逐行解释字节码执行程序的想法。
3.2 JIT(Just-In-Time)编译器
当执行某些频繁被调用的代码(比如for循环中的代码)时,如果按照解释执行,效率非常低,这种被频繁调用的代码成为热点代码。为了提高热点代码的执行效率,在运行时,JIT编译器则会将这些代码编译成与本地平台有关的机器码,并进行各种层次的优化。
JIT的构成组件包括:
- 中间代码生成器——生成中间代码
- 代码优化器——优化中间代码
- 目标代码生成器——生成机器码或本地代码
- 分析器——负责查找热点代码
3.2.1 JIT编译器类型
在 Hotspot 虚拟机中内置了两种JIT编译器:
- C1编译器:主要关注点在于局部性能优化,适用于执行时间短或对启动性能有要求的程序,如:GUI应用,C1编译器也被称为Client Compiler。
- C2编译器:为长期运行的服务端应用程序做性能优化的编译器,适用于执行时间较长或对峰值性能有要求的程序,C2编译器也被称为Server Compiler。
3.2.2 分层编译
在 Java7 引入了分层编译,这种方式综合了 C1 的启动优势和 C2 的峰值性能优势。分层编译将 JVM 的执行状态分为5个层次:
- 第0层:程序解释执行,默认开启性能监控(Profiling),如果不开启,可触发第二层编译
- 第1层:C1编译,将字节码编译成本地代码,进行简单可靠的优化,不开启Profiling
- 第2层:C1编译,开启Profiling,仅执行带方法调用次数和循环回边执行次数Profiling的C1编译
- 第3层:C1编译,执行所有带Profiling的C1编译
- 第4层:C2编译,将字节码编译为本地代码,但会启用一下编译耗时较长的优化,甚至会根据性能监控信息进行一些不可靠的激进优化。
Java8 中默认开启分层编译,-XX:-TieredCompilation 参数可关闭分层编译只使用C2编译,如果只使用C1编译可设置参数 -XX:TieredStopAtLevel=1。
-Xint参数可设置为强制解释器模式运行,-Xcomp可设置为强制运行JIT编译模式。
3.2.3 热点代码探测
Hotspot 虚拟机判定热点代码基于2种计数器进行,方法调用计数器和回边计数器,只有代码符合标准并达到设置的阈值才会进行JIT编译优化。
1)方法调用计数器
当某个方法调用次数达到阈值就会触发JIT编译优化。
jinfo -flag CompileThreshold 或者 java -XX:+PrintFlagsFinal -version 命令可查看方法调用次数阈值,客户端模式下默认值为1500,服务端模式下默认值为10000。

2)回边计数器
用于统计方法体中循环体代码执行次数,字节码中遇到控制流向后跳转的指令称为“回边”(Back Edge),用于计算是否为热点代码的阈值。
计算公式如下:
回边计数器阈值 = 方法调用计数器阈值(CompileThreshold)*(OSR比率(OnStackReplacePercentage)-解释器监控比例(InterpreterProfilePercentage))/ 100
java -XX:+PrintFlagsFinal -version 命令可查看相关参数



根据图中显示的各参数的默认值可以计算出回边计数器阈值为:1000 * (140 - 33) = 10700
3.2.4 编译优化技术
1)方法内联
方法内联的优化行为是将目标方法的代码复制到发起调用的方法中,避免真实方法调用。
private int add(int a, int b, int c, int d) {return addInt1(a, b) + addInt2(c, d);
}
private int addInt1(int a, int b) {return a + b;
}
private int addInt2(int a, int b) {return a + b;
}
例如上面的代码如果被检测为热点代码,则会被优化为以下代码:
private int add(int a, int b, int c, int d) {return a + b + c + d;
}
热点方法不一定都会被内联优化,只有当方法体大小小于参数 -XX:FreqInlineSize 值(默认325字节)才会进行内联,非热点方法当方法体小于参数 -XX:MaxInlineSize 值(默认35字节)才会进行内联。
相关性能调优 :
- 减小热点方法检测阈值,增加内联方法体阈值,缺点则是会增加内存占用
- 尽量避免在一个方法体内写入大量代码,习惯使用小方法体
- 尽量使用final private static 关键字修饰方法,代码优化时,因为继承需要额外的类型检查。
2)锁消除
当方法中的局部方法中创建的对象只能被当前线程访问时,不存在锁竞争,JIT编译会对这个对象的方法进行锁消除。
参数 -XX:+EliminateLocks 可以开启锁消除(默认开启),-XX:-EliminateLocks 则是关闭锁消除。
3)锁粗化
如果检测到同一个对象执行了连续的加锁和解锁操作,则会将这一系列操作合并成一个更大的锁,从而提升程序运行效率。
4)逃逸分析
JIT编译器会对热点代码中的对象进行逃逸分析,分析该对象动态作用域,当被传递到其他方法中称为方法逃逸,当能被外部方法所引用则为线程逃逸。
不逃逸 到 方法逃逸 再到 线程逃逸,逃逸程度由低到高。
逃逸分析可以通过参数 -XX:+DoEscapeAnalysis开启(jdk1.8默认开启),或 -XX:-DoEscapeAnalysis 关闭。
关闭逃逸分析会导致对象分配到堆中,频繁触发垃圾回收导致代码运行慢。
5)标量替换
当确定对象不会逃逸出线程之外,该对象则会被分配到栈上,对象分配到栈需要进行成员变量拆分,这种优化技术叫做标量替换。标量替换需要开启逃逸分析。
标量替换可以通过参数 -XX:+EliminateAllocations开启(jdk1.8默认开启),或-XX:+EliminateAllocations关闭
3.3 垃圾收集器
Java的一个主要特点就是开发人员不需要过分关注对象的内存管理,无用对象的销毁和空间释放都交由JVM的垃圾收集器处理。这减轻了编程负担提高了编程效率,但垃圾回收也会影响程序的性能。
详细介绍见上章。
四、总结
JVM中的执行引擎是JVM的重要组成部分之一,主要负责将字节码指令翻译成机器码指令。
执行引擎包括解释器,JIT解释器和垃圾收集器。
解释器就是运行时的“翻译者”,将字节码解释为机器指令。
但是某些频繁执行的热点代码依然采用解释执行的话会导致程序执行很慢,JIT编译器则是负责将热点代码编译分层优化成本地机器码。
垃圾收集器则负责无用对象的销毁,释放内存空间。
相关文章:

【JVM详解四】执行引擎
一、概述 Java程序运行时,JVM会加载.class字节码文件,但是字节码并不能直接运行在操作系统之上,而JVM中的执行引擎就是负责将字节码转化为对应平台的机器码让CPU运行的组件。 执行引擎是JVM核心的组成部分之一。可以把JVM架构分成三部分&am…...

esp32 udp 客户端 广播
esp32 udp 客户端 广播 #include "bsp_udpc.h"// #include "com_config.h" // #include "com_xqueue.h"#include "bsp_udpc.h" #define TAG "bsp_udpc"#include <string.h> #include <sys/param.h> #include &q…...

nginx日志存储access日志和error保留180天,每晚把前一天的日志文件压缩成tar.gz
logrotate日志分割时,rotate参数是必须要加的 在logrotate的配置文件中,rotate参数用于指定保留的旧日志文件数量。如果不配置rotate参数,默认是0个,也就是只允许存在一份日志,刚切分出来的日志会马上被删除 l…...

【Java】多线程和高并发编程(四):阻塞队列(上)基础概念、ArrayBlockingQueue
文章目录 四、阻塞队列1、基础概念1.1 生产者消费者概念1.2 JUC阻塞队列的存取方法 2、ArrayBlockingQueue2.1 ArrayBlockingQueue的基本使用2.2 生产者方法实现原理2.2.1 ArrayBlockingQueue的常见属性2.2.2 add方法实现2.2.3 offer方法实现2.2.4 offer(time,unit)方法2.2.5 p…...

C#控件开发6—旋转按钮
按钮功能:手自动旋转,标签文本显示、点击二次弹框确认(源码在最后边); 【制作方法】 找到控件的中心坐标,画背景外环、内圆;再绘制矩形开关,进行角度旋转即可获得; 【关…...

在亚马逊云科技上云原生部署DeepSeek-R1模型(下)
在本系列的上篇中,我们介绍了如何通过Amazon Bedrock部署并测试使用了DeepSeek模型。在接下来的下篇中小李哥将继续介绍,如何利用亚马逊的AI模型训练平台SageMaker AI中的,Amazon Sagemaker JumpStart通过脚本轻松一键式部署DeepSeek预训练模…...

C# COM 组件在.NET 平台上的编程介绍
.NET学习资料 .NET学习资料 .NET学习资料 一、COM 组件简介 COM(Component Object Model)即组件对象模型,是一种微软提出的软件组件技术,它允许不同的软件模块在二进制层面进行交互。COM 组件可以用多种编程语言开发࿰…...

火热的大模型: AIGC架构解析
文章目录 一、背景介绍二、架构描述数据层模型层(MaaS)服务层(PaaS)基础设施层(IaaS)应用层 三、架构分析四、应用场景与价值4.1 典型场景4.2 价值体现 五、总结 一、背景介绍 火热的大模型,每…...

Android LifecycleOwner 闪退,java 继承、多态特性!
1. 闪退 同意隐私政策后,启动进入游戏 Activity 闪退 getLifecycle NullPointerException 空指针异常 FATAL EXCEPTION: main Process: com.primer.aa.gg, PID: 15722 java.lang.RuntimeException: Unable to instantiate activity ComponentInfo{com.primer.aa.…...

PHP 完整表单实例
PHP 完整表单实例 引言 表单是网站与用户交互的重要方式,尤其是在收集用户输入数据时。PHP 作为一种流行的服务器端脚本语言,在处理表单数据方面具有强大的功能。本文将提供一个完整的 PHP 表单实例,涵盖表单创建、数据收集、验证和存储等关…...

深度学习学习笔记(32周)
目录 摘要 abstract 1 Mask R-CNN 2 RoI Align 2.1 RoIPool实验 2.2 RoIAlign实验 3 Mask Branch(FCN) 4 其他细节 4.1 Mask R-CNN损失 4.2 Mask分支损失 4.3 Mask Branch预测使用 摘要 Mask R-CNN是2017年发表的文章,一作是何恺明大神,没错就…...

Web3 开发者的机遇与挑战:技术趋势与职业发展
随着 Web3 技术的迅速发展,互联网的未来正朝着去中心化、用户主权、隐私保护等方向演进。作为 Web3 生态的核心力量,Web3 开发者在推动这一变革中扮演着至关重要的角色。无论是在区块链技术、智能合约开发、去中心化应用(DApp)的构…...

探索robots.txt:网站管理者的搜索引擎指南
在数字时代,网站如同企业的在线名片,其内容和结构对搜索引擎的可见性至关重要。而在这背后,有一个默默工作的文件——robots.txt,它扮演着搜索引擎与网站之间沟通桥梁的角色。本文将深入探讨robots.txt的功能、编写方法及其在现代…...

LM Studio本地调用模型的方法
首先需要下载LM Studio,(LM Studio - Discover, download, and run local LLMs)安装好后,需要对index.js文件进行修改,主要是对相关源hugging face的地址修改。 以macOS为例: cd /Applications/LM\ Studi…...

防火墙安全综合实验
防火墙安全综合实验 一、拓扑信息 二、需求及配置 实验步骤 需求一:根据下表,完成相关配置 设备接口VLAN接口类型SW2GE0/0/2VLAN 10AccessGE0/0/3VLAN 20AccessGE0/0/1VLAN List:10 20Trunk 1、创建vlan10和vlan20 2、将接口划分到对应…...

uniapp 编译生成鸿蒙正式app步骤
1,在最新版本DevEco-Studio工具新建一个空项目并生成p12和csr文件(构建-生成私钥和证书请求文件) 2,华为开发者平台 根据上面生成的csr文件新增cer和p7b文件,分发布和测试 3,在最新版本DevEco-Studio工具 文…...

【进程与线程】如何编写一个守护进程
如何编写一个守护进程。我们首先需要理解守护进程是什么。守护进程是在后台运行的进程,通常没有控制终端,用于执行系统任务,比如服务器或者定时任务。 用户可能想创建一个长期运行的服务,比如Web服务器或者日志监控程序。 首先&a…...

ubuntu安装VMware报错/dev/vmmon加载失败
ubuntu安装VMware报错/dev/vmmon加载失败,解决步骤如下: step1:为vmmon和vmnet组件生成密钥对 openssl req -new -x509 -newkey rsa:2048 -keyout VMW.priv -outform DER -out VMW.der -nodes -days 36500 -subj "/CNVMware/"ste…...

web前端布局--使用element中的Container布局容器
前端页面,跟Qt中一样,都是有布局设置的。 先布局,然后再在各布局中添加显示的内容。 Element网站布局容器:https://element.eleme.cn/#/zh-CN/componet/container 1.将element相应的布局容器代码layout,粘贴到vue项…...

手写一个C++ Android Binder服务及源码分析
手写一个C Android Binder服务及源码分析 前言一、 基于C语言编写Android Binder跨进程通信Demo总结及改进二、C语言编写自己的Binder服务Demo1. binder服务demo功能介绍2. binder服务demo代码结构图3. binder服务demo代码实现3.1 IHelloService.h代码实现3.2 BnHelloService.c…...

git rebase发生冲突时 ☞ 解决冲突
参考:特性分支 Rebase 主干分支...

【通俗易懂说模型】反向传播(附多元分类与Softmax函数)
🌈 个人主页:十二月的猫-CSDN博客 🔥 系列专栏: 🏀深度学习_十二月的猫的博客-CSDN博客 💪🏻 十二月的寒冬阻挡不了春天的脚步,十二点的黑夜遮蔽不住黎明的曙光 目录 1. 前言 2. …...

SQL Server查询计划操作符(7.3)——查询计划相关操作符(6)
7.3. 查询计划相关操作符 48)Key Lookup:该操作符对一个有簇索引的表进行书签查找。参数列包含簇索引的名字和用于查找簇索引中数据行的簇键。该操作符总是伴随一个Nested Loops操作符。如果其参数列中出现WITH PREFETCH子句,则查询处理器已决定使用异步预取(预读,read-ah…...

计算机视觉的研究方向、发展历程、发展前景介绍
以下将分别从图像分类、目标检测、语义分割、图像分割(此处应主要指实例分割)四个方面,为你介绍研究生人工智能计算机视觉领域的应用方向、发展历程以及发展前景。 文章目录 1.图像分类应用方向发展历程发展前景 2.目标检测应用方向发展历程…...

反转字符串-双指针法,
在 Java 中,使用 双指针法 反转字符串是一种高效且直观的方法。以下是详细的解析和代码实现。 1. 双指针法的核心思想 使用两个指针:一个指向字符串的起始位置(left),另一个指向字符串的末尾位置(right&…...

亚博microros小车-原生ubuntu支持系列 27、手掌控制小车运动
背景知识 本节跟上一个测试类似:亚博microros小车-原生ubuntu支持系列:26手势控制小车基础运动-CSDN博客 都是基于MediaPipe hands做手掌、手指识别的。 为了方便理解,在贴一下手指关键点分布。手掌位置就是靠第9点来识别的。 2、程序说明…...

STM32 HAL库 CANbus通讯(C语言)
#include "main.h" #include "stm32f1xx_hal.h"CAN_HandleTypeDef hcan; CAN_TxHeaderTypeDef TxHeader; CAN_RxHeaderTypeDef RxHeader; uint8_t TxData[8]; uint8_t RxData[8]; uint32_t TxMailbox;void CAN_Init(void) {// 使能CAN时钟__HAL_RCC_CAN1_C…...

ML.NET库学习005:基于机器学习的客户细分实现与解析
文章目录 ML.NET库学习005:基于机器学习的客户细分实现与解析项目主要目的和原理目的原理 项目概述实现的主要功能主要流程步骤使用的主要函数方法关键技术 主要功能和步骤功能详细解读详细步骤解析 数据集及其处理步骤数据集处理步骤关键处理步骤原理1. 数据清洗与…...

(2/100)每日小游戏平台系列
新增一个猜单词小游戏! ------------------------------------------------------------------------------------------------------------------ 猜单词游戏玩法 游戏规则: 游戏会从一个预设的单词列表中随机选择一个单词。玩家有 6 次机会来猜测单…...

【Linux Oracle】杂货铺 日常实用2024
1.跨服务器移动文件 passwd=^T^bxxxx `/usr/bin/expect <<-EOF set timeout -1 spawn scp -r ${BATCH_TIME} sxnhtc@192.168.3.x:${EXP_MCRO_DIR}/ expect "*password:" send "$passwd\r" interact expect eof EOF` curl -k -X GET https://192.16…...