avatar

一条在知识海洋的咸鱼

这个家伙很懒,啥也没有留下😋

  • 首页
  • Linux
  • OCJP
  • Java核心技术卷
  • J2EE相关标准
  • 深入理解Java虚拟机
  • NIO与SOcket编程技术指南
  • Java多线程编程核心技术
  • Redis开发与运维
  • Spring Cloud Alibaba 微服务原理与实践
  • DevOps
  • Docker
  • MySQL必知必会
  • AI自学路线
  • Spring Boot 编程思想(核心篇)
主页 虚拟机字节码执行引擎
文章

虚拟机字节码执行引擎

发表于 2021-01-28 更新于 2026-06- 10
作者 Administrator
15~20 分钟 阅读
  • 概述
    • 虚拟机与物理机的区别
  • 运行时栈帧结构
    • 局部变量表
    • 操作数栈(Operand Stack)
    • 动态链接
    • 方法返回地址
    • 附加信息
  • 方法调用
    • 解析
    • 分派
      • 静态分派
      • 动态分派
      • 单分派与多分派
      • 虚拟机动态分派的实现
  • 动态类型语言支持
    • 动态类型语言
    • 动静态语言的权衡
    • Java与动态类型
    • java.lang.invoke包
    • invokedynamic指令
    • 实战:掌握方法分派规则
  • 基于栈的字节码解释执行引擎

概述

执行引擎是JVM核心的组成部分之一

虚拟机与物理机的区别

  • 物理机
    执行引擎是直接建立在处理器、缓存、指令集和操作系统层面上的
  • 虚拟机
    执行引擎由软件自行实现的,因此可以不受物理条件制约指令集和执行引擎的结构体系,能够执行不被硬件直接支持的指令集格式

不同的VM实现,执行引擎再执行字节码的时候,通常会有解释执行(通过解释器执行)和编译执行(通过即时编译器产生本地代码执行)2中选择

所有的JVM执行引擎输入、输出都是一致的:输入的是字节码二进制流,处理过程是字节码解析执行的等效过程,输出的是执行结果

运行时栈帧结构

image.png
JVM以方法作为最基本的执行单元,“栈帧”(Stack Frame)则是用于支持JVM进行方法调用和方法执行背后的数据结构

每一个方法从调用开始至执行结束的过程都对应着一个栈帧在JVM栈里面从入栈到出栈的过程

每一个栈帧都包括了局部变量表、操作数栈、动态连接、方法返回地址和一些额外的附加信息
在编译Java程序源码的时候,栈帧中需要多大的局部变量表,需要多多深的操作数栈就已经被分析计算出来,并写入到方法表的Code属性之中

子活动线程中,只有位置栈顶的方法才是在运行的,只有位于栈顶的栈帧才是生效的,其被称为“当前栈帧”(Currenr Stack Frame),与这个栈帧所关联的方法被称为“当前方法”(Current Method)。执行引擎所允许的所有字节码指令都只针对当前栈帧进行操作

局部变量表

局部变量表(Local Variables Table)是一组变量值的存储空间,用于存放方法参数和方法内部定义的局部变量

为了尽可能节省栈帧好用的内存空间,局部变量表中的变量槽是可以重用的,如果当前字节码PC计数器的值已经超出了某个变量的作用域,那么这个变量对应的变量槽就可以交给其他变量来重用

  • 在某些情况下,变量槽的复用会直接影响到系统的垃圾收集行为
/**
 * @author jtao
 * @Date 2021/1/28  11:33
 * @Description   局部变量表槽复用对垃圾收集的影响
 *              VM参数:-verbose:gc
 */
public class TestLocalVariablesTables {

    @Test
    public void testGC() {
        {
            byte[] placehoder = new byte[64 * 1024 * 1024];
            System.gc();
        }
    }


    @Test
    public void testGC1() {
        {
            byte[] placehoder = new byte[64 * 1024 * 1024];
        }
        System.gc();
    }

    @Test
    public void testGC2() {
        {
            byte[] placehoder = new byte[64 * 1024 * 1024];
        }
        int a=0;
        System.gc();
    }
}

对象能否被回收的根本
对上述TestGC、TestGC1、TestGC2方法分别运行得到的结果
image.png
内存没有被回收,因为变量placeholder还处于作用域之内
image.png
placeholder被限制在代码块之内,执行gc的时候已不能被访问,但还是没有被回收
image.png
只有TestGC2的内存被回收

变量能否被回收的根本原因在于:局部变量表中的变量槽是否还存有关于变量数据对象的引用。 前2种情况placeholder的变量槽没有改变,没有被其他变量所引用。所以无法回收,一般这种关联没有被及时打断影响都很轻微。

类的字段变量由2次赋值过程,一次是准备阶段,赋予系统初始值,另一次是初始话阶段,赋予程序员定义的初始值。因此类变量可以不赋值也有初始值,但成员变量不可以,编译无法通过

/**
 * @author jtao
 * @Date 2021/1/28  11:29
 * @Description
 */
public class Test {
    static int a;

    public static void main(String[] args) {
        int b;
        System.out.println(a);
//        System.out.println(b);
    }
}

image.png

image.png

操作数栈(Operand Stack)

操作数栈也成为操作栈,先入后出栈
当一个方法刚刚开始执行的时候,这个方法的操作数栈是空的,在方法的执行过程中,会有各种字节码指令往操作数栈中写入和提取内容,也就是出栈和入栈操作
在概念模型中,两个不同栈帧作为不同方法的虚拟机栈的元素,是完全相互独立的。但是大多数JVM实现都会进行一些优化处理,令2个栈帧出现一部分重叠。让下面的栈帧的部分操作数栈与上面的栈帧的部分局部变量表重叠在一起。可以系欸月空间,且在方法调用时可以直接共用一部分数据,无需进行额外的参数复制传递
image.png

动态链接

每个栈帧都包含一个只想运行时常量池中该栈帧所属方法的引用,持有这个引用是为了支持方法调用过程中的动态连接

方法返回地址

当一个方法开始执行后,只有两种方式退出这个方法。

  • 正常调用完成
    执行引擎遇到任意一个方法返回的字节码指令,如果有返回值则传递给上次的方法调用者
  • 异常调用完成
    执行过程遇到异常,且没有在方法体内得到妥善处理。只要在本方法的异常表中没有搜索到匹配的异常处理器,就会导致方法退出。不会给上层调用者提供任何返回值

方法的推出的过程实际上等同于把当前栈帧出栈

附加信息

《Java虚拟机规范》允许JVM实现增加一些规范里没有描述的信息到栈帧之中,例如与调试、性能收集相关信息

方法调用

该阶段的目的是确定被调用方法的版本(即调用哪一个方法),未涉及方法内部的具体运行过程

解析

方法在程序真正运行之前就有一个可确定的调用版本,并且这个方法的调用版本在运行期是不可改变的。
调用目标在程序代码写好、编译器都进行编译那一刻就已经确定下来。这类方法的调用被称为解析(Resolution)

分派

静态分派

//TODO

动态分派

//TODO

单分派与多分派

//TODO

虚拟机动态分派的实现

//TODO

动态类型语言支持

JDK7的项目目标:实现动态类型语言,是为JDK8里可以顺利实现Lambda表达式而做的技术储备

动态类型语言

  • 动态语言
    关键特征为它的类型检查的主题过程是在运行期间而不是编译器执行的
    如APL、Erlang、Groovy、PHP等
  • 静态语言
    在编译器就进行类型检查过程的语言
    如C++、Java

一门语言的哪一种检查行为要在运行期进行哪一种检查要在编译期进行没有必然的逻辑关系,是由语言规范中认为设立的约定

动静态语言的权衡

  • 静态语言
    能够在编译器确定变量类型,最显著的好处是编译器可以提供全面严谨的类型检查,这样与数据类型相关的潜在问题就能在编码时被即使发现,利于稳定性及让项目容易达到更大的规模
  • 动态语言
    在运行期才确定类型,可以为开发人员提供极大的灵活性,某些在静态类型语言中要花大量臃肿代码来实现的功能,由动态类型语言去做会很清晰简洁

Java与动态类型

动态类型方法调用的底层问题再JVM层次上解决嘴和是

java.lang.invoke包

JDK7时薪加入的包
//TODO

invokedynamic指令

//TODO

实战:掌握方法分派规则

//TODO

基于栈的字节码解释执行引擎

深入理解Java虚拟机
JVM
许可协议: 
分享

相关文章

1月 28, 2021

Java内存模型与线程

概述概述JVM如何实现多线程、多线程之间由于共享和和竞争数据而导致的一系列问题及解决方案多任务处理在许多场景下,让计算机同时去做几件事情1.因为计算机的运算能力强大2.计算机的运算速度比存储和通信速度大多了,太多的时间花费在磁盘I/O、网络通信或者数据库上除要充分利用计算机处理器能力,一个服务端要同

1月 28, 2021

虚拟机字节码执行引擎

概述虚拟机与物理机的区别运行时栈帧结构局部变量表概述执行引擎是JVM核心的组成部分之一虚拟机与物理机的区别物理机执行引擎是直接建立在处理器、缓存、指令集和操作系统层面上的虚拟机执行引擎由软件自行实现的,因此可以不受物理条件制约指令集和执行引擎的结构体系,能够执行不被硬件直接支持的指令集格式不同的VM

1月 26, 2021

虚拟机类加载机制

概述JVM类加载机制动态加载例子类加载的时机固定不变的步骤:除了解析之外类初始化种类被动引用举例接口的加载的区别类加载的过程加载zip压缩包中读取概述在Class文件中描述的各类信息,最终都需要加载到虚拟机中之后才能被运行和使用。JVM类加载机制Java虚拟机把描述类的数据从Class文件加载到内存

下一篇

Java内存模型与线程

上一篇

虚拟机类加载机制

最近更新

  • 55 异常兜底:工具失败、非法输出、空检索与超时
  • 54 Agent Memory:短期记忆、用户上下文与摘要
  • 53 Human-in-the-loop:让关键动作暂停、确认与恢复
  • 52 工单 Tool:字段校验、创建结果与失败补偿
  • 51 订单 Tool:Mock、鉴权、状态解析与脱敏

热门标签

java基础 微服务 maven Spring Tomcat DDD Linux Linux基础 SQL基础 数据结构算法

目录

©2026 一条在知识海洋的咸鱼. 保留部分权利。

使用 Halo 主题 Chirpy