浅谈JVM(五):虚拟机栈帧结构

上一篇:
浅谈JVM(一):Class文件解析
浅谈JVM(二):类加载机制
浅谈JVM(三):类加载器和双亲委派
浅谈JVM(四):运行时数据区

5.虚拟机栈帧结构

在这里插入图片描述

​ 方法是程序执行的最小单元,每个方法被执行时都会创建一个栈帧,用于存储局部变量表、操作数栈、动态连接、方法出口(返回值、异常)等信息。每一个方法被调用直至退出的过程就对应着栈帧在虚拟机栈中从入栈到出栈的过程。

图片

5.1.局部变量表

​ 局部变量表(Local Variables)所需空间在编译阶段就已经分配,局部变量表中的存储单位为槽(Slot),可以类比数组中的槽,通过索引访问槽中的变量,索引从0开始。局部变量表可存放的数据类型有8种基本数据类型(boolean、byte、short、char、int、long、float、double)、引用类型(reference)和returnAddress(旧版JDK中的类型,指向字节码指令的地址,曾用于处理异常时的跳转,现已基本不用),其中double和long两种数据类型的变量占用两个槽,其余类型占用一个槽。虚拟机规范中并没有明确规定一个变量槽占用空间的大小,变量槽的长度随处理器、操作系统或虚拟机实现不同而变化。

​ 假设某变量需要占用两个变量槽,就用相邻的第N个和第N+1个变量槽进行存储,虚拟机不允许单独访问其中的某一个,如果遇到这样的操作,在类加载的校验阶段就会抛出异常。

​ 如果执行的是实例方法(无static修饰的方法),那么局部变量表的0号槽位默认是方法所属对象的实例引用,也就是通过"this"可访问到的隐含参数。其余参数按照参数列表的顺序,从索引1开始分配变量槽。参数列表分配完毕之后,再根据方法体内部定义的变量顺序和作用域分配其余变量槽。

​ 局部变量表中的索引是槽的索引,比如实例方法定义两个变量,double a和int b,this引用占用0号槽,a是double占用两个槽(1和2),所以变量b的索引就是3。

package com.menglaoshi.test;

/**
 * @author 专治八阿哥的孟老师
 */
public class TestClass02 {
    public void test(int a, String s) {
        int b = 1;
    }
}

在这里插入图片描述

​ 局部变量表的变量槽是可以复用的,以便节省栈帧消耗的内存空间。方法体中定义的变量,其作用域不一定覆盖整个方法,程序计数器的数值超出变量作用域后,这个变量占用的空间就可以被其他变量复用。

public static void main(String[] args) {
    {
        byte[] arr = new byte[64 * 1024 * 1024];
    }
    int a = 1;
}

在这里插入图片描述

​ 上面的代码共有三个局部变量,String[] args、byte[]arr和int a。但局部变量表的槽位只有两个,因为是静态方法没有this,所以0号槽位存放的是方法入参args;arr引用数值先被存入了第2个变量(槽1),由于arr的作用域只在大括号内,所以当超出其作用域后,定义变量a时,a复用了arr的变量槽,也存入了槽1。

​ 局部变量表的设计某些情况也会影响垃圾回收。在虚拟机运行参数中添加"-verbose:gc",查看垃圾回收过程:

public static void main(String[] args) {
    {
        byte[] arr = new byte[64 * 1024 * 1024];
    }
    System.gc();
}

在这里插入图片描述

​ 从上面的代码可以看到, 执行System.gc()的时候,arr占用的64MB空间并没有被回收,这是因为执行System.gc()的时候,虽然已经离开了变量作用域,但局部变量表没有发生重写操作,局部变量表中还有arr的引用(槽1),所以没有被回收。在System.gc()前面添加一行代码"int a=1;",可以看到arr占用的空间被回收了。通过前面的分析我们可以知道,局部变量表此时重写,变量a复用了arr的槽位(槽1)。

在这里插入图片描述

​ 与成员变量不同的是,局部变量如果只定义未赋值(如int a;),并不会在连接的准备阶段赋"零值",局部变量不赋值就没有默认值,所以局部变量不赋值就不能使用。

5.2.操作数栈

​ 操作数栈(Operand Stacks)是一个后入先出(Last In First Out,LIFO)的栈结构,操作数栈的最大深度在编译的时候被写入到Code属性的max_stacks中,32位数据类型所占的栈容量为1,64位数据类型所占的栈容量为2,操作数栈的深度不会超过在max_stacks中设定的最大值。

​ 当方法开始执行时,操作数栈是空的,在方法的执行过程中,会有各种字节码指令向操作数栈中写入和提取内容,也就是出栈和入栈操作。以加法为例:

public static void main(String[] args) {
    int i = 10;
    int j = 20;
    int k = i + j;
}

在这里插入图片描述

​ 入栈出栈过程如下,BIPUSH将整数常量压入栈顶;ISTORE将栈顶元素出栈,并存入局部变量表对应索引位置;ILOAD加载局部变量表指定索引位置元素压入栈顶;IADD将栈顶两个元素出栈,计算相加结果之后将结果压入栈顶。

在这里插入图片描述

操作数栈中的元素数据类型必须与字节码指令的序列严格匹配,编译器在编译过程中必须严格保证这一点,并且类加载过程中的校验阶段也验证这一点。比如IADD命令要求栈顶的两个元素必须是int,不可以是其他类型。

​ 调用其他方法时也是通过操作数栈传递参数的。如果该方法不是本地方法,则从操作数堆栈中弹出参数列表对应的参数值(nargs),在Java虚拟机栈上为所调用的方法创建一个新栈帧。nargs参数值将连续设置为新帧的局部变量的值,实例方法this是局部变量0,arg1是局部变量1,而静态方法arg1位于局部变量0,依此类推。并且程序计数器被设置为要调用的方法的第一条指令的操作码,继续执行方法的第一条指令。

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

在这里插入图片描述

5.3.动态连接

每个栈帧都包含对当前方法类型的运行时常量池的引用,以支持方法代码的动态连接(Dynamic Linking)。Class文件的常量池中存有大量的符号引用,动态连接将这些符号引用转换为具体的方法引用。这些符号引用一部分会在类加载阶段或者第一次使用的时候就被转化为直接引用,这种转化被称为静态解析;另外一部分将在每一次运行期间都转化为直接引用,这部分就称为动态连接。具体内容会在后续文章中讲解。

5.4.方法出口

​ 方法执行后,有两种可能的退出方式,一种是正常返回退出,一种是出现异常退出。

​ 正常调用完成(Normal Method Invocation Completion)是指方法运行过程中没有出现异常(包括虚拟机内部异常和手动抛出的异常),方法正常返回。如果当前方法有返回值,正常调用完成后会向调用者返回一个值。

​ 异常调用完成(Abrupt Method Invocation Completion)是指方法执行过程中遇到异常,并没有在方法体内得到妥善处理的情况。异常调用完成的方法永远不会给调用者返回任何值。

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

​ 方法退出的过程实际上等同于把当前栈帧出栈,因此退出时可能执行的操作有:恢复上层方法的局部变量表和操作数栈,把返回值(如果有)压入调用者栈帧的操作数栈中,调整程序计数器的值以指向方法调用指令后面的一条指令等。

5.5.附加信息

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

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

驱动开发:内核使用IO/DPC定时器

本章将继续探索驱动开发中的基础部分,定时器在内核中同样很常用,在内核中定时器可以使用两种,即IO定时器,以及DPC定时器,一般来说IO定时器是DDK中提供的一种,该定时器可以为间隔为N秒做定时,但如…

内卷?焦虑?35岁?找不到工作?端正态度激励一下正在挣扎的Android程序员

前言 亲爱的各位Android程序员,您们好: 我理解您们的焦虑和困惑,但我想告诉您的是:作为一名Android程序员,您依然是非常有前途和市场需求的职业人才。 首先,您要知道,移动互联网时代的普及率…

【数据结构】时间复杂度和空间复杂度

👦个人主页:Weraphael ✍🏻作者简介:目前学习C和算法ing ✈️专栏:【数据结构】 🐋 希望大家多多支持,咱一起进步!😁 如果文章对你有帮助的话 欢迎 评论💬 点…

1662_MIT 6.828 JOS check_page_free_list实现分析以及boot_alloc问题修复

全部学习汇总: GreyZhang/g_unix: some basic learning about unix operating system. (github.com) 继续尝试完善分析JOS的代码中存储管理的部分。 上次看到了这里,本来想先去看看这两个函数实现。但是缺失了调用场景,感觉理解也不一定准确。…

对拍程序 并查集专题 (C++ | 洛谷 | acwing | 蓝桥)

文章目录【蓝桥杯专题】 (C | 洛谷 | acwing | 蓝桥)1249. 亲戚836. 合并集合837. 连通块中点的数量238. 银河英雄传说 【带权并查集】145. 超市 【并查集 贪心】4793. 危险程度 (连通块并查集 )普通oi 读文件对拍程序【蓝桥杯专题】 &#…

树和二叉树相关的练习(选择题)

目录 一、二叉树 二、堆 三、遍历二叉树 一、二叉树 某二叉树共有 399 个结点,其中有 199 个度为 2 的结点,则该二叉树中的叶子结点数为( )。 A. 不存在这样的二叉树 B. 200 C. 198 D. 199 下列数据结构中,不适合…

C++ Primer Plus 学习笔记(八)——输入、输出和文件

1 流和缓冲区 C程序把输入和输出看作字节流。输入时,程序从输入流中抽取字节;输出时,程序将字节插入到输出流中。 缓冲区是用作中介的内存块,它是将信息从设备传输到程序或从程序传输给设备的临时存储工具,通过使用缓…

HTTP协议:当下最主流的应用层协议之一,你确定不了解一下吗?

一.HTTP协议的含义http是什么?超文本传输协议(Hyper Text Transfer Protocol,HTTP)是一个简单的请求-响应协议,它通常运行在TCP之上。‘超’可以理解为除了文本之外的图片,音频和视频,和一些其他…

STM32基于HAL工程FREERTOS读取DS18B20数据+串口输出

STM32基于HAL工程FREERTOS读取DS18B20数据串口输出✨申明:本文章仅发表在CSDN网站,任何其他网站,未注明来源,见此内容均为盗链和爬取,请多多尊重和支持原创!🍁对于文中所提供的相关资源链接将作不定期更换。…

无需公网IP,远程连接SQL Server数据库【内网穿透】

文章目录1.前言2.本地安装和设置SQL Server2.1 SQL Server下载2.2 SQL Server本地连接测试2.3 Cpolar内网穿透的下载和安装2.3 Cpolar内网穿透的注册3.本地网页发布3.1 Cpolar云端设置3.2 Cpolar本地设置4.公网访问测试5.结语1.前言 数据库的重要性相信大家都有所了解&#xf…

现代前端开发者的自我迷失,你还会前端基础知识吗?

通常来说,我认为情况并不算糟糕,熟练的手可以几乎做到一切。然而,最近我注意到一些事情改变了我对这个行业的看法。似乎在这些无尽的趋势、范式和新奇玩意中,我们忘记了前端开发的支柱(意思是忘记了基础知识&#xff0…

【python】GIL全局锁

一、原理: 全局解释器锁(Global Interpreter Lock,GIL)规定全局范围内任意时候一个进程里只能同时执行一个线程。每一个线程在执行时,都会锁住GIL,以阻止别的线程执行;执行一段时间后&#xff…

OBCP第四章 SQL调优-SQL执行性能监控

(g)v$sql_audit 全局 SQL 审计表 基于虚拟表__all_virtual_sql_audit的视图, 该虚拟表对应的数据存放在一个可配置的内存空间中 由于存放这些记录的内存是有限的,因此到达一定内存使用量,会触发淘汰 可以用来查看每次请求客户端来源&…

【操作系统复习】第3章 处理机调度与死锁 3

死锁(Deadlock):指多个进程在运行过程中因争夺资源而造成的一种僵局,当进程处于这种僵持状态时,若无外力作用,这些进程都将永远不能再向前推进。 对资源不加限制地分配可能导致进程间由于竞争资源而相互制约…

JavaSE学习总结(十三)Set集合HashSet集合LinkedHashSet集合TreeSet集合比较器的使用利用Set集合实现去重

JavaSE学习总结(十三)Set集合/HashSet集合/LinkedHashSet集合/TreeSet集合/比较器的使用/利用Set集合实现去重 一、Set集合 Set集合是Collection集合的一个子接口,实际上Set就是Collection,只是行为略有不同: Set集…

VUE3项目实现动态路由demo

文章目录1、创建vue项目2、安装常用的依赖2.1 安装elementUI2.2 安装axios2.3 安装router2.4 安装vuex2.5 安装store2.6 安装mockjs3、编写登录页面以及逻辑4、编写首页以及逻辑5、配置router.js6、配置store.js7、配置menuUtils.js(动态路由重点)8、配置…

树的前序遍历与中序遍历构造二叉树和树的中序遍历与后序遍历构造二叉树

目录 一.树的前序遍历与中序遍历构造二叉树 1.题目描述 2.问题分析 3.代码实现 二.树的中序遍历与后序遍历构造二叉树 1.题目描述 2.问题分析 3.代码实现 三.问题思考 一.树的前序遍历与中序遍历构造二叉树 1.题目描述 给定两个整数数组 preorder 和 inorder &#xf…

【机器学习】Logistic回归---学习笔记

Logistic回归学习笔记Logistic回归学习线路预备知识:建议先去B站学习一下信息量,熵,BL散度,交叉熵的概念。Logistic回归的函数模型损失最小化架构分类函数最大概率分类函数阈值分类函数Logistic回归的优化算法梯度下降随机梯度下降…

4.5--计算机网络之基础篇--2.网址到网页解析--(复习+深入)---好好沉淀,加油呀

1.浏览器做的第一步工作是解析 URL 对 URL 进行解析,从而生成发送给 Web 服务器的请求信息 URL? URL 实际上是请求服务器里的文件资源 当没有路径名时,就代表访问根目录下事先设置的默认文件,也就是 /index.html 或者 /default.html 这些文件…

计算机网络复习笔记(三)物理层

文章目录一物理层的基本概念四大特性:两种信号:调制和编码传输介质三大部分二物理层的基本通信技术四种信道复用技术数据的传输方式三OSI模型一物理层的基本概念 四大特性: 机械特性 接口是怎么样的 电气特性 用多少伏的电 功能特性 线路上…