栈帧结构详解

article/2025/10/18 22:28:19

前言

栈帧结构
  Java虚拟机以方法作为基本的执行单位,“栈帧”是用于支持虚拟机进行方法调用和执行的数据结构,每一个方法从调用开始到执行结束,都对应着一个栈帧在虚拟机栈里面从入栈到出栈的过程,栈帧也是虚拟机运行时数据区中虚拟机栈的栈元素。位于栈顶的栈帧被称为“当前栈帧”,其对应的方法称为“当前方法”。
  栈帧中存储了方法的局部变量表、操作数栈、动态连接、方法返回地址和附加信息(例如,调试、性能收集相关的信息,取决于具体的虚拟机实现,是《Java虚拟机规范》中未描述的信息)。


以下对栈帧中的各个部分进行具体的阐述

一、局部变量表

  局部变量表是一组变量值的存储空间,用于存储方法参数和方法内部定义的局部变量。在Java程序被编译为.Class文件时,在方法的Code属性中的max_locals中确定了该方法所需要分配的局部变量表的最大容量。
  在《深入理解Java虚拟机》中,关于局部变量表的叙述内容有很多,我在通读一遍之后仍然感觉有一点晦涩,于是加以整理归纳为以下要点,足以囊括书中对局部变量表的描述。

1、局部变量表以变量槽(Variables Slot)为其最小单位用来表示大小。

2、《Java虚拟机规范》中并没有明确提及变量槽应该占用多大的空间,但是有引导性的指出其大小至少要能存放一个boolean(1位)、byte(8位)、char(16位)、short(16位)、int(32位)、float(32位)、reference、returnAddress类型的数据,其中reference类型没有明确指出其大小,它表示一个对象实例的引用,returnAddress表示指向一条字节码指令的地址。所以一个变量槽可以这样认为,其可以存放一个32位以内的数据类型,但并不是说规定了每个变量槽占用32位的空间,注意区别说法。

3、对于64位的long和double类型来说,Java虚拟机会以高位对齐的方式为其分配两个连续的变量槽空间,因为是自连续的,所以Java虚拟机不允许以任何的方式单独访问其中一个变量槽,否则会抛出异常。这里分配两个连续变量槽,意味着读写的时候是两个变量槽,但是由于局部变量表所在的栈帧其所在的虚拟机栈是线程私有的,所以哪怕读写两个变量槽是不是原子性的,都不会有线程安全问题。

4、Java虚拟机通过索引定位的方式来使用局部变量表,从0到局部变量表最大的变量槽数量,如果是32位的数据类型变量,索引N就代表使用了第N个变量槽,如果是64位,那么说明会同时使用第N和N+1两个变量槽。

5、当一个方法被调用时,Java虚拟机会使用局部变量表来完成实参到形参的传递。如果虚拟机调用的是一个实例方法(非static修饰),那么索引位置0的变量槽默认就是用于传递方法所属对象实例的引用,可以在方法中使用关键字“this”来访问这个隐含的参数。其余参数按照参数表顺序,从索引1开始分配变量槽,剩下的变量槽按照方法体内定义的变量顺序分配。

6、变量槽可以重用以节省栈帧消耗的内存空间,方法体中的定义的变量,其作用域并不一定会覆盖整个方法,如果当前字节码PC计数器的值已经超出某个变量的作用域,那么这个变量对应的变量槽就可以交给其他变量重用。这一段大部分都是深入理解Java虚拟机的原话,我个人通俗的理解就是,PC计数器的值超过某个变量的作用域,意思就是这个变量已经被执行或者使用过了,完成了它的任务,那么变量自然也就不再需要变量槽的空间了,将变量槽的空间给其他变量复用。关于变量槽的复用,书中还有一起其他描述,例如某些情况下变量槽复用会影响垃圾收集行为,有兴趣的朋友可以取翻一翻,个人觉得读一读了解就行了。

7、最后一点大家都耳熟能详,局部变量不能没有初始值,定义的时候必须设置初始值。

二、操作数栈

  操作数栈又叫做操作栈,是一个后入先出的栈。同局部变量表一样,操作数栈的最大深度也在编译的时候被写入到Code属性的max_stacks数据项中。其可以归纳出以下几个要点。

1、操作数栈的每一个元素都可以是包括long和double在内任意的Java数据类型。32位数据类型占的栈容量为1,64位占的栈容量为2。

2、当一个方法刚执行的时候,这个方法的操作数栈是空的,方法执行的过程中,各种字节码指令往操作数栈写入和提取内容,对应着入栈和出栈的操作。例如执行整数相加的操作时,字节码指令iadd(i代表int类型,add表示相加)要求在运行的时候操作数栈中最近接栈顶的两个元素必须是int同类型(操作数栈中元素的数据类型必须与字节码指令的序列严格匹配)且已经存入操作数栈,执行指令的时候,将两个整数出栈(取出),相加后的结果在入栈(存入)。

3、在概念模型中,两个不同栈帧作为不同方法的虚拟机栈的元素,是完全互相独立的。但是大多数虚拟机的实现会有一些优化处理,另两个栈帧出现一部分重叠。让下面栈帧的部分操作数栈与其上面那个栈帧的部分局部变量表重叠在一起,不仅可以节约一部分空间,重要的是方法调用的时候可以直接共用一部分数据,无须进行额外的参数复制传递。
栈帧之间的数据共享

三、动态连接

  每个栈帧都包含一个指向运行时常量池中该栈帧所属方法的引用,持有这个引用是为了方法调用过程中的动态连接。Class文件中的常量池中存有大量的符号引用,字节码中的方法调用指令就是以常量池里指向方法的符号引用作为参数的,这些符号的一部分会在类加载阶段或者第一次使用的时候被转化为直接引用,这种转化被称为静态解析。另一部分在每一次运行期间转化为直接引用,被称为动态连接(因为方法的执行对应着栈帧的入栈出栈,所以存放在栈帧中)

四、方法返回地址

1、;当一个方法开始执行后,只有两种方式退出这个方法。第一种是执行引擎遇到任意的一个方法返回的字节码指令,这个时候可能会有返回值需要返回给方法的上层调用者(主调方法)。是否有返回值及返回何种类型,由具体的方法返回指令决定。这种退出方式被称为“正常调用完成”。

2、另一种退出方式是方法执行过程中遇到异常,并且异常没有在方法体内得到妥善处理。无论是虚拟机内部产生的异常还是代码中使用athrow字节码指令产生的异常,只要在本方法的异常表中没有搜索到匹配的异常处理器,就会导致方法退出,称为“异常调用完成”,其不会给它的上层调用者提供任何返回值。

3、无论何种方式退出,方法退出之后都必须返回最初方法被调用的位置,程序才能继续执行,方法返回时可能需要在栈帧中保存一些信息,用来恢复其上层主调方法的执行状态。正常退出时,主调方法的PC计数器的值就可以作为返回地址,栈帧中很可能(视具体的虚拟机而定)会保存这个计数器值。异常退出时,返回地址需要通过异常处理器表确定,栈帧中一般就不会保存这部分信息。

4、方法退出意味着当前栈帧出栈,因此退出时可能(视具体的虚拟机而定)执行的操作有:恢复上层方法的局部变量表和操作数栈,把返回值压入调用者栈帧的操作数栈,调整PC计数器的值以指向方法调用指令后面的一条指令,等操作。

五、附加信息

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


http://chatgpt.dhexx.cn/article/7A1t7mF9.shtml

相关文章

浅谈栈帧

一、 什么是栈帧? 什么是栈帧,首先引用百度百科的经典解释:“栈帧也叫过程活动记录,是编译器用来实现过程/函数调用的一种数据结构。 实际上,可以简单理解为:栈帧就是存储在用户栈上的(当然内…

栈帧(Stack Frame)

0x01.栈在计算机中的应用 在计算机系统中,栈也可以称之为栈内存是一个具有动态内存区域,存储函数内部(包括main函数)的局部变量和方法调用和函数参数值,是由系统自动分配的,一般速度较快;存储地址是连续且…

什么是栈帧

栈帧浅析 什么是栈帧 引用百度百科中的解释: 栈帧也叫过程活动记录,是编译器用来实现过程/函数调用的一种数据结构。函数的每次调用,都有它自己独立的栈帧。栈帧中维持着函数调用所需要的各种信息,包括函数的入参、函数的局部变…

【详解】函数栈帧——多图(c语言)

目录 前言 一.函数栈帧是什么? 二、栈帧准备知识 1.内存分区 2.什么是栈? 3.esp,ebp,eax寄存器 三、详解栈帧创建与销毁全过程 调用函数之前: 将传入函数的值放入栈中 函数执行: 1.保护当前ebp 2.…

【mcuclub】模数转换ADC0832

一、实物图 二、原理图 编号名称功能1CS片选使能,低电平芯片使能。2CH0模拟输入通道0,或作为IN/-使用。3CH1模拟输入通道1,或作为IN/-使用。4GND电源地5DI数据信号输入,选择通道控制。6DO数据信号输出,转换数据输出。7…

[技术讨论] [DDS] AD9833原理介绍及chiliDDS驱动分享(上)

​ 其实本文还有另一标题:《AD9833调不通?看这篇**就够了》 总觉字里行间隐隐霸气外露,不符合作者低调的风格,于是换了个朴素标题。标题狂不狂暂且不评,作者水平有限却是个事实;看到这篇**是你我缘分&…

ADC0832的使用

百度搜索ADC0832 Datasheet便可以免费获取该芯片的权威数据手册。 最重要的是查看它的时序及对应英文词组的意思。 CLK为时钟信号,需要外部输入,可直接与单片机引脚相连 Chip Select(CS):从Timing图中可以看出芯片工作期间要保持…

ADC0832的AD模数转换原理及编程

✅作者简介:嵌入式领域优质创作者,博客专家 ✨个人主页:咸鱼弟 🔥系列专栏:单片机设计专栏 目录 一、描述 二、模数转换原理: 三、模数转换的过程: 四、八位串行A/D转换器ADC0832简介&…

ADC0809的使用

一、前言介绍 使用ADC0809对一个模拟电压进行转换转换后的电压使用数码管显示出来 二、ADC0809的介绍 1、ADC0809简介 ADC0809是采用COMS工艺制造的双列直插式单片8位A/D转换器。分辨率8位,精度7位,带8个模拟量输入通道,有通道地址译码锁…

基于STM32F103RCT6的AD9833驱动开发(代码可以免费发邮箱)

基于STM32F103RCT6的AD9833驱动开发(代码可以免费发邮箱) AD9833手册分析 管脚定义: 手册就先讲到这里,不明白的欢迎评论区留言,另外我会把代码还有手册一并发送给感兴趣的朋友。 AD9833典型应用电路&#x…

AD9833数字信号发生器模块

简 介: 本文记录了使用快速制版测试AD9833这款数字信号发生器的内容。 关键词: AD9388,数字信号发生,快速制版 基于AD9833的正弦波,三角波,方波频率发生模块可以通过ZIGBEE来完成输入输出控制。其中还包括有…

STM32驱动AD9833模块

STM32驱动AD9833模块 前言软硬件准备一、本次使用的硬件二、代码 链接 前言 淘宝上买了个AD9833模块,stm32用商家的例程代码可以调频,可以调相,就是调不了幅度。换了几块不同32开发板都不行,重新以正点原子F103的工程为基础把驱动…

AD9833信号波形谐波

AD9833产生高频信号的谐波 ~ AD9833是一款AnalogDevices公司提供的数字信号可编程信号发生器芯片。它一般配有外置的主时钟信号,每次时钟信号将将内部28位的相位累加器递增一个相位数值。该相位数值由芯片SPI串口被外部的MCU设置。 相位累加器的高12位选择内部4096…

51驱动AD9833

使用51驱动AD9833模块的使用 关于AD9833相关参数程序流程代码片上传程序总结 原文链接:https://www.yourcee.com/newsinfo/2925703.html 关于AD9833 AD9833是一款低功耗、可编程波形发生器,能够产生正弦波、三角波和方波输出。各种类型的检测、信号激励…

Arduino + AD9833 波形发生器

Arduino SI5351 方波发生器_姜戈12的博客-CSDN博客SI5351 方波发生器https://blog.csdn.net/jiangge12/article/details/125815044 感觉 Si5351 只有方波还是少点意思。 看到有人做 AD9833 ,成品卖355元。https://www.bilibili.com/video/av463721457/ 上面视频…

[技术讨论][DDS] AD9833原理介绍及chiliDDS驱动分享(上)

其实本文还有另一标题:《AD9833调不通?看这篇**就够了》 总觉字里行间隐隐霸气外露,不符合作者低调的风格,于是换了个朴素标题。标题狂不狂暂且不评,作者水平有限却是个事实;看到这篇**是你我缘分&#xff…

STM32单片机TFT显示AD9833 DDS信号发生器语音播报正弦波方波三角波

实践制作DIY- GC0146---TFT显示AD9833 DDS信号发生器 基于STM32单片机设计---TFT显示AD9833 DDS信号发生器 二、功能介绍: 硬件组成:STM32F103C系列最小系统板 1.8寸TFT彩屏AD9833信号模块4*4矩阵键盘DY-SV17F语音播报模块 1.通过4*4键盘来设定频率值和…

失真很大的波形发生器AD9833

波形发生器AD9833 01 波形发生器 一、AD9833 AD9833是一款AnalogDevices公司提供的数字信号可编程信号发生器芯片。 它一般配有外置的主时钟信号,每次时钟信号将将内部28位的相位累加器递增一个相位数值。 该相位数值由芯片SPI串口被外部的MCU设置。  相位累加器…

【STM32+cubemx】0030 HAL库开发:DDS芯片AD9833实现简单的波形发生器

大家好,我是学电子的小白白,今天带大家了解一款波形发生器芯片——AD9833。 AD9833是AD公司出品的一款DDS波形发生器,能够产生正弦波、三角波和方波输出。 1)什么是DDS 通俗来讲,DDS是一种把波形预先存储在芯片内部的…

2.电赛进行时......(AD9833(DDS)模块的学习使用)

文章目录 前言一、DDS是什么(信号发生器)?二、AD9833模块介绍1.概述2.模块移植3、效果展示![在这里插入图片描述](https://img-blog.csdnimg.cn/2b0d29939275403f9248339c894965a0.png#pic_center) 前言 如果是玫瑰,它总会开花的…