JVM---执行引擎
概述
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 | /** * 【执行引擎-概述】 * what? * Execution Engine; * 执行引擎 是JVM核心的组成部分之一; * * 虚拟机与物理机的 执行引擎: * 同: * 都有代码执行能力; * 异: * 物理机: * 执行引擎 直接建立在 处理器、缓存、指令集、OS 上的; * 虚拟机: * 执行引擎 由软件自行实现,不受物理条件制约地 定制指令集及执行引擎的结构体系,能够执行不被硬件直接支持的指令集格式; * * JVM的主要任务是 负责装载字节码到JVM内部,但 字节码并不能直接运行在OS上; * (字节码指令并非等价于本地机器指令,JVM内部的只是能被JVM识别的字节码指令、符号表、其他辅助信息...) * * 作用: * 将 字节码指令 解释/编译 为 对应平台的本地机器指令; * * 工作过程: * 执行引擎 在执行过程中 需要执行什么样的字节码指令 完全依赖于 PC寄存器; * 每当执行完一条字节码指令,PC寄存器 就会更新下一条需要被执行的 字节码指令地址; * 方法在执行过程中,执行引擎 可能会通过 存储在局部变量表中的对象引用 准确定位到 存储在堆区的对象实例信息; * 通过 对象头中的 元数据指针 定位到 目标对象的类型信息; */ |
Java代码编译和执行过程
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 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 | /** * 【执行引擎-Java代码编译和执行的过程】 * 大部分的程序代码 转换成 物理机的目标代码(或虚拟机能执行的指令集)之前,都需要经过以下各个步骤: * * 程序源码 -> 词法分析 -> 单词流 -> 语法分析 -> 抽象语法树 -> 指令流(可选) -> 解释器 -> 解释执行 * 【javac实现】 【字节码解释器interpreter】 * * -> 优选器(可选) -> 中间代码(可选) -> 生成器 -> 目标代码 * 【JIT编译器】 * * 解释器 interpreter: * 当JVM 启动时 会根据 预定义的规范 对字节码 采用逐行解释的方式执行; * (将 每条字节码指令 翻译 为 对应平台的本地机器指令执行) * * JIT编译器 Just In Time Compiler: * JVM 将 源代码 直接编译成 对应平台的本地机器指令; * * <机器指令->汇编语言->高级语言->字节码> * * 机器指令: * what: * CPU能直接识别并执行的指令; * (二进制编码方式 表示) * * 组成: * 操作码 * 指令的功能 * (该指令要完成的操作) * * 操作数 * 参与运算的对象 * * 特点: * 机器指令 与 CPU密切相关,不同种类的CPU对应的机器指令也不同; * * * 指令: * 由于 机器指令是0和1 组成的二进制序列,可读性太差,于是人们发明了指令; * 指令 就是 把机器码的0和1序列,简化为 对应的指令(mov,inc...),可读性稍好; * 不同的硬件平台,执行同一个操作,对应的机器指令可能不同; * * * 指令集: * 每个平台所支持的指令,称为 对应平台的指令集; * eg: * x86指令集,对应 x86架构的平台; * arm指令集,对应 arm架构的平台; * * 汇编语言: * 由于 机器指令 可读性太差,于是 发明了 汇编语言; * * 在汇编语言中,用 助记符代替机器指令的 操作码, * 用地址符号或标号代替指令或操作数的地址; * * 特定的汇编语言和特定的机器语言指令集是一一对应的,不同平台之间不可直接移植; * * 工作方式: * 在不同的硬件平台中,汇编语言对应着不同的机器语言指令集,通过汇编过程转换成机器指令; * * * * 高级语言: * 一种 独立于机器,面向过程或对象的语言; * * 工作方式: * 高级语言设计的程序必须经过 解释或编译 为 本地机器指令 以后才能被机器执行; * * eg: * C、C++ 源程序执行过程经历2个阶段: * 1、编译: 源代码 -> 汇编代码 * 读取 源程序,进行词法和语法分析,将高级语言指令转换为功能等效的汇编代码; * * 2、汇编: 汇编代码 -> 本地机器指令 * 把 汇编代码 翻译成 本地机器指令; * * 字节码: * what? * 中间码; * 二进制代码; * 比 机器指令更抽象; * * 实现方式: * javac 将 Java源代码 编译为 字节码指令; * * 工作方式: * 由 执行引擎 对字节码指令 进行 解释/编译 为对应平台的机器指令,交由CPU执行; */ |
执行方式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | /** * 【执行引擎-执行方式】 * 设置执行引擎执行方式: * 当VM启动时,解释器首先发挥作用,不必等待JIT编译器全部编译完成再执行,这样可以省去很多不必要的编译时间; * * 并且随着程序运行时间的推移,JIT编译器逐渐发挥作用,根据热点探测功能,将有价值的字节码编译为本地机器指令,获得更高的执行效率; * * 设置执行引擎执行方式: * 默认Hotspot VM采用 解释器与JIT编译器 并存的架构; * * 自定义执行方式: * -Xint: * 完全采用 解释器模式 执行程序; * * -Xcomp: * 完全采用 JIT编译器模式 执行程序,如果JIT出现问题,解释器会介入执行; * * -Xmixed: * 采用 解释器+JIT编译器 混合模式 执行程序; * */ |
解释器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | /** * 【执行引擎-解释器 interpreter】 * 工作方式: * 当JVM 启动时 会根据 预定义的规范 对字节码指令 采用逐行解释的方式执行; * (将 每条字节码指令 翻译 为 对应平台的本地机器指令执行) * * 解释器分类: * 字节码解释器: * (Java源码 -> 字节码) -> (C++代码 -> 本地机器指令) * javac 执行引擎-字节码解释器 * * 模板解释器: * */ |
即时编译器
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 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 | /** * 【执行引擎-即时编译器 Just In Time Compiler】 * * what? * 前端编译器 * .java -> .class * eg:javac * * 后端编译器 * 字节码指令 -> 本地机器指令 * eg:Hotspot VM的C1、C2 * * JIT分类: * Hotspot VM中,有2个JIT编译器: * Client Compiler(C1编译器)、Server Compiler(C2编译器) * * 自定义使用某个JIT编译器: * -client * * 指定JVM在client模式下运行,并使用 C1编译器; * * C1编译器 对字节码 进行简单可靠的优化,耗时短; * * -server * * (64位默认使用 Server模式) * 指定JVM在server模式下运行,并使用 C1编译器; * * C2编译器 耗时较长的优化,以及激进优化; * * C1编译器 和 C2编译器 不同优化策略: * C1: * 方法内联 * 将引用的函数代码编译到引用点处,这样可以减少栈桢的生成,减少参数传递以及跳转过程; * 去虚拟化 * 对唯一的实现类进行内联; * 冗余消除 * 在运行期间把一些不会执行的代码折叠掉 * * C2: * 优化主要是在全局层面,逃逸分析是优化的基础; * 基于逃逸分析的几种优化: * 标量替换: * 用标量值 代替聚合对象的属性值 * 栈上分配: * 对于未逃逸对象分配在栈 * 同步消除: * 清除同步操作,通常指synchronized * * JIT编译器执行方式: * 将 JIT编译请求 放入队列中(VM_THREAD),由一个异步线程监听处理队列内容 * */ |
分层编译
1 2 3 4 5 | /** * 【执行引擎-JIT编译器-分层编译】 * Java 7开始引入了分层编译的概念; * 结合了C1和C2的优势,追求启动速度和峰值性能的一个平衡; */ |
热点代码及探测方式
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 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 | /** * 【执行引擎-JIT编译器-热点代码及探测方式】 * 是否需要启动JIT编译器将字节码直接编译为本地机器指令,需要根据代码被调用执行的频率而定; * * 热点代码: * 需要被编译为本地机器指令的字节码; * * JIT编译器 在运行时 会对 频繁被调用的热点代码做出深度优化,将其直接编译为本地机器指令,以提升Java程序执行性能; * * ***哪些字节码是热点代码? * 一个被调用多次的方法; * 一个方法体内循环次数较多的循环体; * * ***一个方法究竟被调用多少次(或一个循环体执行多少次循环)才能达到热点标准? * 依靠热点探测功能; * * 热点探测功能实现: * Hotspot VM采用 基于计数器的热点探测; * Hotspot VM为每个方法都建立2个不同类型的计数器: * a,方法调用计数器: * 统计方法调用次数 * * 默认阈值 client模式 1500次,server模式 10000次 * * 设置方法调用计数器阈值: * -XX:CompileThreshold * * <热度衰减> * 如果不做任何阈值设置,方法调用计数器统计的并不是方法被调用的次数,而是一个相对的执行频率(一段时间内方法被调用次数); * * 当超过一定的时间限度,如果方法的调用次数仍不足以让它提交给JIT编译器编译,那这个方法的调用计数器就被减少一半,这个过程称为方法调用计数器的热度衰减,这段时间称为方法统计的半衰周期; * * 热点衰减 是在虚拟机进行垃圾回收时顺便进行的; * * 关闭热点衰减: * -XX:-UseCounterDecay * * 关闭后,只要系统运行时间足够长,绝大部分方法都被编译为本地机器指令; * * 设置半衰周期的时间: * -XX:CounterHalfLifeTime ,单位秒 * * b,回边计数器: * 统计循环体执行的循环次数 * * <热点代码缓存> * * java -XX:+PrintFlagsFinal -version | grep CodeCache * * 位置: * 方法区 * * 默认大小: * server 2496KB起 * client 160KB起 * * 最大大小: * */ |
疑问
1 2 3 4 5 6 7 8 9 10 11 | /** * 【疑问】 * 1、为什么Java是半编译半解释型语言? * a, Java源码 -> 字节码 (javac) * b, 执行引擎 解释执行/编译执行 字节码 * * 2、既然Hotspot VM中已经内置了JIT编译器了,为什么还需要性能低的解释器? * 当程序启动后,解释器可以马上发挥作用,省去编译时间,立即执行; * * JIT编译器想发挥作用,需要把代码编译为本地机器指令,需要一定执行时间; */ |
方法调用计数器
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· .NET10 - 预览版1新功能体验(一)
2021-04-07 《计算机网络原理-西安交通大学》---网络体系结构
2020-04-07 asm
2020-04-07 cglib
2020-04-07 Javaassist