深入解析JVM字节码执行引擎

JVM 字节码执行引擎。它是 JVM 核心组件之一,负责实际执行加载到内存中的字节码指令。你可以将它想象成 JVM 的“CPU”。

核心职责:

  1. 加载待执行的字节码: 从方法区(元空间)获取已加载类的方法字节码。
  2. 创建和管理栈帧: 在方法调用时,在 Java 虚拟机栈上为该方法创建一个栈帧,用于存储该方法的执行状态和数据。
  3. 解释执行: 读取字节码指令,逐条解释并执行其对应的本地机器码操作。
  4. 即时编译: 识别热点代码(频繁执行的代码),将其编译成本地机器码(Native Code)并缓存,后续执行直接运行高效的机器码。
  5. 处理结果: 执行完成后,处理返回值(如果有),销毁栈帧,返回调用点。

关键概念与工作机制:

1. 栈帧 (Stack Frame)

  • 本质: 是 JVM 进行方法调用方法执行的数据结构。每次方法调用,都会创建一个新的栈帧并压入当前线程的 Java 虚拟机栈 (Java Virtual Machine Stack)。方法执行结束(无论正常返回还是异常抛出),其栈帧会被弹出并销毁。
  • 构成: 一个栈帧包含以下几个核心部分:
    • 局部变量表 (Local Variable Array):
      • 一个数组,用于存储方法参数方法内部定义的局部变量
      • 索引从 0 开始。
      • longdouble2 个槽位 (Slot),其他基本类型 (int, float, char, short, byte, boolean, reference) 和 returnAddress1 个槽位
      • 方法参数按顺序排在局部变量表的前面(static 方法第 0 位是第一个参数;实例方法第 0 位是 this 引用,然后是参数)。
    • 操作数栈 (Operand Stack):
      • 一个后进先出 (LIFO) 的栈结构。
      • 字节码指令执行的主要工作场所。
      • 指令从操作数栈弹出 (Pop) 操作数进行计算,再将结果压入 (Push) 栈顶。
      • 例如,iadd 指令会弹出栈顶两个 int 值相加,再将结果 int 值压入栈顶。
      • 其深度在编译期就已确定(存储在方法的 Code 属性中)。
    • 动态链接 (Dynamic Linking):
      • 栈帧内部包含一个指向运行时常量池 (Runtime Constant Pool) 中该栈帧所属方法符号引用的指针。
      • 在方法执行过程中,需要将符号引用(如调用的方法名、字段名)解析 (Resolve) 为实际的直接引用(方法入口地址、字段偏移量)。
      • 动态的含义在于,这个解析过程可以在类加载的解析阶段完成,也可以在第一次使用该符号引用时才完成(延迟解析)。
    • 方法返回地址 (Return Address):
      • 存储方法正常完成后需要返回的位置(通常是调用该方法指令的下一条指令地址)。
      • 如果方法异常退出(未捕获的异常),返回地址由异常处理器表 (Exception Table) 确定。
    • 附加信息 (可选): 一些虚拟机实现可能包含调试信息、性能监控数据等。

2. 基于栈的指令集架构

  • JVM 字节码指令集是 基于栈 (Stack-Based) 的,而不是基于寄存器 (Register-Based) 的(如 x86、ARM 汇编)。
  • 优势:
    • 可移植性: 不依赖特定硬件的寄存器数量和结构,指令更紧凑(一个字节操作码)。
    • 简单性: 编译器生成字节码更简单(只需考虑栈操作)。
    • 实现简单: 解释器或 JIT 编译器实现相对容易。
  • 劣势:
    • 执行效率: 完成相同操作通常需要更多指令(频繁的入栈、出栈操作)。
    • 优化难度: 栈操作隐含了更多数据依赖关系,增加了编译器优化的复杂度(但 JIT 可以克服)。

3. 字节码解释执行

  • 过程: 执行引擎包含一个字节码解释器
    1. 定位当前要执行的字节码指令(程序计数器 PC 指向它)。
    2. 读取操作码 (Opcode)。
    3. 根据操作码找到对应的操作(本地机器码片段或微程序)。
    4. 如果需要操作数,从操作数栈弹出。
    5. 执行操作。
    6. 将结果(如果有)压入操作数栈。
    7. 更新 PC 指向下一条指令。
  • 优点: 启动快,内存占用相对小。
  • 缺点: 执行速度慢(每条指令都需要取指、解码、执行本地操作)。

4. 即时编译器 (Just-In-Time Compiler - JIT)

  • 目的: 解决解释执行效率低的问题。将热点代码 (Hot Spot Code) - 频繁执行的方法或循环体 - 动态编译成本地机器码,后续执行直接运行高效的机器码。
  • 工作流程:
    1. 监控: JVM 启动时,解释器执行所有代码,同时 Profiler 监控代码执行频率。
    2. 识别热点: 当某个方法或代码块的调用/执行次数超过阈值(-XX:CompileThreshold),它就被标记为热点代码。
    3. 编译排队: 热点代码被提交给 JIT 编译器线程进行编译。
    4. 编译: JIT 编译器将字节码编译成本地机器码。
    5. 缓存: 编译后的机器码存储在 Code Cache 区域(位于堆外内存)。
    6. 替换: 该方法的入口地址被替换为指向编译好的机器码。
    7. 执行: 后续对该方法的调用直接执行本地机器码,无需解释。
  • HotSpot VM 的 JIT 编译器:
    • C1 编译器 (Client Compiler / -client):
      • 优化较少,编译速度快。
      • 关注局部优化(如方法内联、去虚拟化、冗余消除)。
      • 适合桌面应用或对启动速度敏感的场景。
    • C2 编译器 (Server Compiler / -server):
      • 优化激进,编译速度慢。
      • 进行大量全局优化(如逃逸分析、循环展开、锁消除)。
      • 生成代码执行效率高。
      • 适合服务器端长期运行的应用。
    • 分层编译 (Tiered Compilation - -XX:+TieredCompilation, Java 7+ 默认):
      • 结合 C1 和 C2 的优势。
      • 代码首先被解释执行 (Level 0)。
      • 达到一定调用次数,由 C1 快速编译,开启简单优化 (Level 1, 2, 3)。
      • 如果方法调用非常频繁(成为“更热的点”),再交给 C2 进行深度优化编译 (Level 4)。
      • 目标: 在启动速度和峰值性能之间取得最佳平衡。
  • JIT 关键技术:
    • 方法内联 (Method Inlining): 将被调用方法的代码“复制”到调用方法中,消除方法调用的开销(压栈、跳转、弹栈)。最重要的优化之一!
    • 逃逸分析 (Escape Analysis): 分析对象的作用域。
      • 如果对象不会逃逸出方法或线程(即仅在方法内部使用,或只被当前线程访问),则可进行优化:
        • 栈上分配 (Scalar Replacement): 将对象拆解成基本类型,直接在栈上分配其成员变量,避免堆分配开销和 GC 压力。
        • 同步消除 (Lock Elision): 如果对象不会逃逸到其他线程,对其进行的同步操作(synchronized)可以移除。
    • 公共子表达式消除 (Common Subexpression Elimination): 消除重复计算。
    • 循环展开 (Loop Unrolling): 减少循环条件判断次数。
    • 去虚拟化 (Devirtualization): 将虚方法调用(invokevirtual, invokeinterface)转换为直接调用(invokespecial, invokestatic)或静态调用,消除动态分派开销。基于类层次分析 (CHA)。

5. 方法调用与分派

  • 字节码中调用方法使用特定的指令:
    • invokestatic: 调用静态方法。
    • invokespecial: 调用构造方法 (<init>)、私有方法、父类方法 (super.method())。静态绑定
    • invokevirtual: 调用对象的实例方法(最常见的虚方法调用)。动态绑定
    • invokeinterface: 调用接口方法。动态绑定
    • invokedynamic (Java 7+): 动态语言支持(如 Lambda 表达式、方法引用),由 bootstrap 方法在运行时动态解析调用点。最灵活的绑定
  • 静态分派 (Static Dispatch): 依赖静态类型 (Static Type / Apparent Type) 进行方法版本选择。发生在编译期。典型应用:方法重载 (Overload)
    class Human {}
    class Man extends Human {}
    class Woman extends Human {}public void sayHello(Human guy) { System.out.println("Hello, guy!"); }
    public void sayHello(Man guy) { System.out.println("Hello, gentleman!"); }
    public void sayHello(Woman guy) { System.out.println("Hello, lady!"); }Human man = new Man(); // 静态类型是Human, 实际类型(运行时类型)是Man
    sayHello(man); // 输出 "Hello, guy!"。编译期根据静态类型Human确定调用sayHello(Human)
    
  • 动态分派 (Dynamic Dispatch): 依赖实际类型 (Actual Type / Runtime Type) 进行方法版本选择。发生在运行期。典型应用:方法重写 (Override)。通过虚方法表 (vtable) 实现(invokevirtual, invokeinterface)。
    abstract class Animal {abstract void makeSound();
    }
    class Dog extends Animal { void makeSound() { System.out.println("Woof!"); } }
    class Cat extends Animal { void makeSound() { System.out.println("Meow!"); } }Animal animal = new Dog(); // 实际类型是Dog
    animal.makeSound(); // 输出 "Woof!"。运行期根据实际类型Dog查找Dog的makeSound方法
    animal = new Cat();
    animal.makeSound(); // 输出 "Meow!"。运行期根据实际类型Cat查找Cat的makeSound方法
    

6. 执行引擎如何与内存交互

  • 栈帧管理: 在 Java 虚拟机栈上分配和销毁,存储方法执行状态(局部变量、操作数栈)。
  • 堆 (Heap): 执行引擎通过字节码指令(如 new, getfield, putfield, arraylength)在堆上创建和操作对象/数组。对象字段的访问通过解析后的直接引用(偏移量)进行。
  • 方法区 (Metaspace): 存储已被加载的类信息、常量、静态变量、即时编译器编译后的代码缓存 (Code Cache)。执行引擎从这里读取要执行的字节码和符号引用(后续解析)。
  • 程序计数器 (PC Register): 每个线程私有,指向当前线程正在执行的字节码指令地址。执行引擎依赖它知道下一条要执行的指令。

总结:

JVM 字节码执行引擎是 Java 程序运行的动力核心,它通过:

  1. 栈帧管理: 为每个方法调用创建独立上下文(局部变量表、操作数栈等)。
  2. 基于栈的指令集: 定义了可移植但相对低效的执行方式。
  3. 解释执行: 提供快速启动能力。
  4. 即时编译 (JIT): 将热点代码编译成本地机器码,大幅提升执行效率(C1/C2/分层编译 + 多种优化如内联、逃逸分析)。
  5. 方法调用与分派: 正确处理静态分派(重载/编译期)和动态分派(重写/运行期/虚方法表)。
  6. 内存交互: 与 JVM 内存区域(堆、栈、方法区、PC)紧密协作完成数据存取和指令执行。

正是解释器与 JIT 编译器的高效协作,以及基于栈的灵活架构,使得 JVM 能够在跨平台的同时,为 Java 应用程序提供接近原生代码的执行性能。理解执行引擎是深入掌握 JVM 工作原理和进行性能调优的关键。

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

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

相关文章

华为OD机试-MELON的难题-DFS(JAVA 2025A卷)

题意是从N快雨花石中找出最少拿出雨花石的块数&#xff0c;使得雨花石可以均分&#xff0c;直接使用dfs解决此类组合问题 package com.example.demo.bean;import java.util.Arrays; import java.util.LinkedList; import java.util.Scanner;public class YuHuaStone {public s…

鸿蒙数据库操作

一、使用关系型数据库实现数据持久化&#xff0c;需要获取一个RdbStore&#xff0c;其中包括建库、建表、升降级等操作。 const STORE_CONFIG: relationalStore.StoreConfig {name: AnyOffice.db, // 数据库文件名securityLevel: relationalStore.SecurityLevel.S1, // 数据库…

基于ARM SoC的半导体测试

ARM SoC&#xff08;System on Chip&#xff09; 是一种集成了多个关键计算组件的单片系统芯片&#xff0c;广泛应用于移动设备、嵌入式系统、物联网&#xff08;IoT&#xff09;和半导体测试设备等领域。它的核心设计理念是“高度集成”&#xff0c;将处理器、内存、外设接口等…

JavaEE->多线程2

目录 一、线程安全&#xff08;重点&#xff09; 1.线程安全演示 2.线程不安全的原因 1.线程是抢占式执行的&#xff08;执行顺序是随机的&#xff09; 2.多个线程同时修改了同一个变量 3.原子性 4.内存可见性 5.指令重排序&#xff08;有序性&#xff09; 二、解决线…

Flutter TCP通信

启动TCP服务 Future<void> startServer() async {final server await ServerSocket.bind(InternetAddress.anyIPv4, 12345);print(Server listening on ${server.address}:${server.port});server.listen((Socket socket) {print(Client connected: ${socket.remoteAddr…

flask拆分计划

两个启动链接&#xff0c;看日志提示是因为2次启动&#xff0c;一次是database&#xff0c;一次是xmind2&#xff0c;去掉一次就可以&#xff0c;如何去掉一次&#xff1f; 这里启动也调用了一次&#xff0c;所以测试环境注释掉&#xff0c;如下图&#xff0c;也就调用了一次

【生活】ECMO原理、作用、费用及使用方法

博客目录 一、ECMO 是什么&#xff1f;二、ECMO 的作用1. 替代肺功能&#xff08;氧合与二氧化碳清除&#xff09;2. 替代心脏功能&#xff08;循环支持&#xff09;3. 为其他治疗争取时间4. 用于心肺复苏&#xff08;ECPR&#xff09; 三、ECMO 的费用1. 设备使用费2. 耗材费用…

Profinet转EtherCAT网关模块怎么用:案例分享

在某制造工厂西门子S7-1200 PLC中&#xff0c;存在一个技术难题&#xff0c;即伺服驱动器与可编程逻辑控制器&#xff08;PLC&#xff09;之间的通讯不兼容问题。具体而言&#xff0c;PLC采用的是PROFINET通讯协议&#xff0c;而伺服EtherCAT协议驱动器则需要EtherCAT协议进行数…

什么是 NLP-NLP基础知识体系的系统认知

NLP基础知识体系的系统认知 一、引言 今天的学习内容集中于自然语言处理&#xff08;NLP&#xff09;的基本概念、发展历程、核心任务及文本表示技术。通过这一学习过程&#xff0c;我对NLP这门学科有了更加系统和深入的认识&#xff0c;并且理解了NLP技术的广泛应用及其复杂…

数据结构 学习 链表 2025年6月14日08点01分

单向链表: 线性数据结构 由一系列节点组成 每个节点包含: 数据部分:存储实际数据 指针部分:储存指向下一个节点的引用 特点1,每个节点只有一个指向下一个节点的指针 特点2,只能从头到尾 单向遍历 特点3,不需要连续的内存空间 特点4,插入和删除效率高 特点5,随机访问 效率低 …

使用 Kubernetes 部署 PHP 留言板应用(含 Redis 架构)

使用 Kubernetes 部署 PHP 留言板应用&#xff08;含 Redis 架构&#xff09; 文章目录 使用 Kubernetes 部署 PHP 留言板应用&#xff08;含 Redis 架构&#xff09;教程概述技术架构特点 准备工作环境要求 Redis 数据库部署Redis 主从架构原理创建 Redis 领导者 Deployment部…

MATLAB提供的两种画误差矩阵的函数

MATLAB在统计学和机器学习工具包中提供了两种画误差矩阵&#xff08;Confusion matrix&#xff09;的函数。 figure; plotconfusion(YValidation,YPred)figure; cm confusionchart(YValidation,YPred) cm.Title Confusion Matrix for Validation Data; cm.RowSummary row-n…

【Java学习笔记】泛型

泛型 一、泛型的引出 代码示例 public class pra {public static void main(String[] args) {ArrayList arrayList new ArrayList();arrayList.add("java");arrayList.add("jack");arrayList.add("jom");arrayList.add(new a());for (Object…

SpringMVC系列(一)(介绍,简单应用以及路径位置通配符)

0 引言 作者正在学习SpringMVC相关内容&#xff0c;学到了一些知识&#xff0c;希望分享给需要短时间想要了解SpringMVC的读者朋友们&#xff0c;想用通俗的语言讲述其中的知识&#xff0c;希望与诸位共勉&#xff0c;共同进步&#xff01; 1 SpringMVC介绍 SpringMVC本质上…

Java中如何使用lambda表达式分类groupby

Java中如何使用lambda表达式分类groupby Java中如何使用lambda表达式分类groupby分类问题场景传统手写方式lambda使用groupBy()方法一行结束&#xff01;&#xff01;&#xff01;完整代码 Java中如何使用lambda表达式分类groupby 分类问题场景 比如一群学生根据性别和年龄排…

无人机开发分享——无人机集群基于braft实现长机动态推选算法

在无人机集群项目的算法开发中&#xff0c;推选长机作为集群的动态中心&#xff0c;往往承担着集群管理、通讯中继等重要功能。由于通讯链路的有限性和任务的实时性需要&#xff0c;需要保证动态长机时刻工作正常&#xff0c;并在异常情况下快速切换新长机。 本文主要分享基于b…

python 解码 jwt

import base64 import jsondef base64url_decode(base64url_data):# 将URL安全的base64编码数据转换为标准的base64编码数据base64_data base64url_data.replace(-, ).replace(_, /)# 如果数据长度不是4的倍数&#xff0c;则补齐padding_length 4 - len(base64_data) % 4base…

腾讯云TCCA认证考试报名 - TDSQL数据库交付运维工程师(MySQL版)

数据库交付运维工程师-腾讯云TDSQL(MySQL版)认证 适合人群&#xff1a; 适合从事TDSQL(MySQL版)交付、初级运维、售前咨询以及TDSQL相关项目的管理人员。 认证考试 单选*40道多选*20道 成绩查询 70分及以上通过认证&#xff0c;官网个人中心->认证考试 查询 考试费用&am…

Spring Boot的Security安全控制——认识SpringSecurity!

Spring Boot的Security安全控制 在Web项目开发中&#xff0c;安全控制是非常重要的&#xff0c;不同的人配置不同的权限&#xff0c;这样的系统才安全。最常见的权限框架有Shiro和Spring Security。Shiro偏向于权限控制&#xff0c;而Spring Security能实现权限控制和安全控制…

深入理解ArrayList:从Java原生实现到手写一个ArrayList

Java原生ArrayList解析 基本结构 Java的ArrayList是基于数组实现的动态列表&#xff0c;主要特点包括&#xff1a; 动态扩容&#xff1a;当元素数量超过当前容量时&#xff0c;自动扩容&#xff08;通常增加50%&#xff09; 快速随机访问&#xff1a;通过索引访问元素的时间…