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

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.xdnf.cn/news/8685.html

相关文章:

  • 15.1 【基础项目】使用 HTML、CSS 和 TypeScript 构建的简单计数器应用
  • LLM之Agent:Mem0的简介、安装和使用方法、案例应用之详细攻略
  • C# Windows Forms应用程序-002
  • # 使用 Hugging Face Transformers 和 PyTorch 实现信息抽取
  • 数据结构第2章 (竟成)
  • 神经网络加上注意力机制,精度反而下降,为什么会这样呢?注意力机制的本质是什么?如何正确使用注意力机制?注意力机制 | 深度学习
  • 清山垃圾的3个问题
  • 6.4.1最小生成树
  • 第二章网络io
  • 对WireShark 中的EtherCAT抓包数据进行解析
  • C语言指针进阶:通过地址,直接修改变量的值
  • iOS App启动优化(冷启动、热启动)
  • 2025年渗透测试面试题总结-匿名[实习]安全工程师(安全厂商)(题目+回答)
  • 【HTML-12】HTML表格常用属性详解:从基础到高级应用
  • 显存不够?节约显存高效微调语言模型的五种方法及实验
  • 0基础 Git 代码操作
  • 黑马k8s(十六)
  • 题目 3325: 蓝桥杯2025年第十六届省赛真题-2025 图形
  • whisper相关的开源项目 (asr)
  • 动态规划-蓝桥杯-健身
  • Apache OFBiz 17.12.01 的远程命令执行漏洞 -Java 反序列化 + XML-RPC 请求机制
  • MCP技术体系介绍
  • ETL工具:Kettle,DataX,Flume,(Kafka)对比辨析
  • Java高频面试之并发编程-20
  • 03. C#入门系列【变量和常量】编程世界里的“百变魔盒”与“永恒石碑”
  • XSS脚本攻击-DDoS僵王博士-SQL注入-考试周前的邮件
  • C 语言学习笔记
  • python的pip怎么配置的国内镜像
  • CodeBuddy实现图片压缩工具
  • 第 29 场 蓝桥·算法入门赛