当前位置: 首页 > article >正文

JVM规范之栈帧

JVM规范之栈帧

  • 前言
  • 正文
    • 概述
    • 局部变量表
    • 操作数栈
    • 动态链接
  • 总结
  • 参考链接

前言

上一篇文章了解了JVM规范中的运行时数据区:
JVM规范之运行时数据区域
其中,栈是JVM线程私有的内存区,栈中存储的单位是帧(frames),本篇文章通过JVM8规范学习栈帧在JVM运行时的作用。

正文

概述

每个栈帧的内存分配自线程私有的 Java 虚拟机栈(JVM Stack),线程调用方法时压栈,方法结束时出栈销毁。

帧的作用是保存方法调用的局部变量、中间结果、执行动态链接、方法返回信息、发送异常。一个帧通常包含局部变量表、操作数栈、运行时常量池引用,这些组件都是帧私有的。

创建/销毁时机:
当方法被调用的时候,栈帧也会被创建,当方法调用完成时,栈帧被销毁,方法调用的含义包含两种:

  • 方法正常调用结束,方法运行期间,JVM 没有抛出或者没有显式地使用throw抛出未被捕获的异常;
  • 方法非正常结束(如抛出未捕获异常)时,栈帧会提前销毁,JVM 通过异常栈轨迹(StackTrace)记录各层栈帧信息,用于调试;

内存分配策略:
局部变量表和操作数栈的大小在编译时确定,运行时常量池引用的大小本质是一个指针,具体大小可能取决于具体的 JVM 实现,因此栈帧的大小由编译时确定的局部变量表和操作数栈的理论最大值,结合 JVM 实现的内存布局(如 Slot 字节数、对齐策略)决定。

局部变量表

  • 每个栈帧都有自己的局部变量表,局部变量表的大小是在编译时确定的,因此在运行时可以一次性从栈上进行分配;
  • 局部变量表通过索引的方式访问,每个索引的位置可以理解一个变量槽(slot),每个槽可以存储boolean, byte, char, short, int, float, reference, returnAddress类型的值,一对slot可以存储longdouble类型的值;
  • longdouble占用两个连续的变量槽,比如一个long占用了索引nn+1两个变量槽的位置,但是n+1位置是不可读取的,可以被写入,这会导致变量槽n位置的数据失效;
  • JVM 规范没有限制局部变量表中的数据必须进行字节对齐;
  • 在调用方法时,JVM 使用局部变量表传递参数,从索引位置 0 开始,如果调用的方法是一个实例方法,索引 0 位置总是被传入this;对于静态方法,局部变量表索引 0 不存储this,直接从索引 0 开始存储方法参数。

操作数栈

  • 每个栈帧都包含一个操作数栈,栈的最大深度是编译时确定的,当栈帧被创建时,操作数栈是空的;
  • 操作数栈符合栈的特点,LIFO,操作数栈中每个entry可以容纳一个JVM数据类型,包含longdouble类型;
  • 操作数栈中存储的数据类型和操作指令必须严格匹配,JVM会在进行class文件验证时,检查操作数栈的使用是否符合规范;
  • 操作数栈中,long/double作为 64 位值,占用 2 个深度单位,其他类型占 1 个;

动态链接

什么是动态链接?
在 JVM 中,动态链接类加载机制运行时环境的关键环节,主要用于将符号引用(Symbolic References)解析为直接引用(Direct References),是实现多态的核心技术。

  • 每个栈帧都包含一个运行时常量池的引用,用于实现方法调用的动态链接过程;
  • 字节码文件中描述了方法调用的符号表引用,但是并没有实际代码的内存地址,因此动态链接是将符号表引用翻译为直接引用;
  • 动态链接是实现多态的核心机制,通过运行时常量池解析虚方法的符号引用,在运行时根据对象实际类型(如instanceof)找到具体方法的直接引用。例如,子类重写父类方法时,编译期符号引用指向父类,运行时动态链接到子类实现。

补充:

  • 在编译阶段,字节码文件中仅保存符号表引用,不会涉及静态链接或者动态链接的过程;
  • 静态链接在类加载阶段(解析阶段)确定方法调用目标,将符号表引用转化为直接引用,而动态链接指JVM在运行时才能确定调用目标的实例类型,在运行时将符号表引用转化为直接引用。

动态链接代码示例

public class Animal {public void sound() {System.out.println("Animal"); } // 虚方法
}
public class Dog extends Animal {@Overridepublic void sound() {System.out.println("Woof");} // 动态链接目标
}
// 调用时通过运行时常量池解析为Dog.sound()
Animal animal = new Dog();
animal.sound(); // invokevirtual指令触发动态链接

总结

本篇文章根据JVM 8规范了解了栈帧的数据结构:

  • 栈帧是在JVM线程进行方法调用时创建,每调用一个方法就会创建一个栈帧,每当方法调用结束或者异常结束,栈帧会被出栈;
  • 每个栈帧通常包含局部变量表、操作数栈、运行时常量池引用,栈帧的内存占用大小仅取决于JVM的实现,其中,局部变量表类似一个数组,JVM线程通过索引的方式读取局部变量表的内容;操作数栈用于保存中间结果和方法保存结果,要求操作数栈中的数据类型和指令必须匹配;运行时常量池引用是JVM实现动态链接的关键,当然也用来支持类字段引用和其它常量的访问;

JVM 规范仅定义栈帧的逻辑结构(如局部变量表、操作数栈),具体实现(如 Slot 是否对齐、指针压缩)由厂商决定。例如,HotSpot 中 Slot 通常为 32 位,long/double占 2 个 Slot,无需 64 位对齐。

参考链接

jvm8s

http://www.lryc.cn/news/2386604.html

相关文章:

  • 【C++指南】string(四):编码
  • 深度学习之序列建模的核心技术:LSTM架构深度解析与优化策略
  • AI量化交易是什么?它是如何重塑金融世界的?
  • 分布式事务处理方案
  • CVE-2024-36467 Zabbix权限提升
  • Dify中的自定义模型插件开发例子:以xinference为例
  • crud方法命名示例
  • 尚硅谷redis7 33-36 redis持久化之RDB优缺点及数据丢失案例
  • No such file or directory: ‘ffprobe‘
  • 计算机网络-WebSocket/DNS/Cookie/Session/Token/Jwt/Nginx
  • 功能“递归模式”在 C# 7.3 中不可用,请使用 8.0 或更高的语言版本的一种兼容处理方案
  • 第4章-操作系统知识
  • 将网页带格式转化为PDF
  • 【ArcGIS】ArcGIS AI 助手----复现
  • 使用 FFmpeg 将视频转换为高质量 GIF(保留原始尺寸和帧率)
  • 《Java vs Go vs C++ vs C:四门编程语言的深度对比》
  • 充电枪IEC62196/EN 62196测试内容
  • 有效的字母异位符--LeetCode
  • SAP ERP 系统拆分的七大挑战
  • AcrelEMS 3.0智慧能源管理平台:构建企业微电网数智化中枢
  • 【HTML-12】HTML表格常用属性详解:从基础到高级应用
  • Word转PDF--自动生成目录
  • MySQL组合索引优化策略
  • Spring MVC 的的核心原理与实践指南
  • 轻量级视觉语言模型 Dolphin:高效精准的文档结构化解析利器
  • 如何安全配置数据库(MySQL/PostgreSQL/MongoDB)
  • 将 Docker 镜像从服务器A迁移到服务器B的方法
  • git merge解冲突后,add、continue提交
  • Lines of Thought in Large Language Models
  • 八股战神-JVM知识速查