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

在这里插入图片描述

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

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

相关文章

关于CH32F203程序下载方式说明

关于CH32F203程序下载方式说明🎉好久没有写有关wch单片机的相关内容了,具体焊接完2块CH32F203,发现烧写程序遇到了各种囧事。 📓CH32F203程序下载方式 🔨通过串口下载。接口为PA9和PA10不知道是不是各厂商之间默契的规…

Linux VIM编辑器常用指令

普通模式的基本指令 按键作用yy 复制一行 通常会与p一起使用p将复制的内容写出 数字yy 从当前行往下数数字行进行复制y^复制当前行的起始位到光标的前一位y$复制光标当前位置到行末尾yw复制光标所在的位置之后(包括光标)的(不完整&#xff0…

ffmpeg关于视频前几秒黑屏的问题解决

关于音频播放器视频前两秒黑屏的解决,及QtAV和ffmpeg的环境搭建(软件包可以找李青璠提供,也可以自己下)首先我们可以参考下面两个博客进行ffmpeg的搭建,第一个博客的问题可以在第二个博客里寻求方法解决。其中第一个博…

多线程的锁策略

文章目录前言一.乐观锁与悲观锁二.轻量级锁和重量锁三.自旋锁和挂起等待锁四.互斥锁和读写锁五.不可重入锁和可重入锁六.公平锁和非公平锁前言 其实这里指的锁策略,不只只是线程才存在的。也不只是针对Java的,我现在就即将介绍常见的锁策略。 一.乐观锁…

Python 自动化指南(繁琐工作自动化)第二版:八、输入验证

原文:https://automatetheboringstuff.com/2e/chapter8/ 输入验证代码检查用户输入的值,比如来自input()函数的文本,格式是否正确。例如,如果您希望用户输入他们的年龄,您的代码不应该接受无意义的答案,如负…

中间表示- 三地址码

使用三地址码的编译器结构 三地址码的基本思想 (1)给每个中间变量和计算结果命名,没有复合表达式 (2)只有最基本的控制流,没有各种控制结构(if、do、while、for等等),只…

2 新建工程步骤

2 新建工程步骤 0.建立工程文件夹 选择一个程序储存文件,新建一个2-1 STM32工程模板文件夹,在2-1 STM32工程模板文件夹新建一个Start,User,Library文件夹 1.Keil中新建工程,选择型号 打开keil5,project->new pr…

045:cesium加载OpenStreetMap地图

第045个 点击查看专栏目录 本示例的目的是介绍如何在vue+cesium中加载加载OpenStreetMap地图。 直接复制下面的 vue+cesium源代码,操作2分钟即可运行实现效果. 注意OpenStreetMap国内加载有问题,需要曲线救图。 文章目录 示例效果配置方式示例源代码(共79行)相关API参考:…

询问ChatGPT的高质量答案艺术——提示工程指南(更新中……)

目录前言一、提示工程简介二、提示技巧2-1、生成法律文件2-2、添加提示技巧三、角色扮演3-1、智能手机产品描述3-2、添加角色扮演四、标准提示4-1、写一篇有关于新智能手机的评论4-2、添加标准提示、角色提示、种子词提示等等五、示例很少、或者没有示例5-1、生成一个手机配置六…

什么是服务架构?微服务架构的优势又是什么?

文章目录1.1 单体架构1.2 微服务架构1.3 单体架构和微服务架构的区分1.4 两种服务架构的优劣点1.4.1 单体架构1.4.2 微服务架构1.5 总结1.1 单体架构 单体架构(Monolithic Architecture)是一种传统的应用程序架构模式,它指的是将一个应用程序…

聚会Party

前言 加油 原文 聚会常用会话 ❶ He spun his partner quickly. 他令他的舞伴快速旋转起来。 ❷ She danced without music. 她跳了没有伴乐的舞蹈。 ❸ The attendants of the ball are very polite. 舞会的服务员非常有礼貌。 ❶ Happy birthday to you! 祝你生日快乐!…

剪枝与重参第四课:NVIDIA的2:4剪枝方案

目录NVIDIA的2:4 pattern稀疏方案前言1.稀疏性的研究现状2.图解nvidia2-4稀疏方案3.训练策略4.手写复现4.1 大体框架4.2 ASP类的实现4.3 mask的实现4.4 模型初始化4.5 Layer嵌入稀疏特性4.6 优化器初始化4.7 拓展-dynamic function assignment4.8 完整示例代码总结NVIDIA的2:4 …

做了个springboot接口参数解密的工具,我给它命名为万能钥匙(已上传maven中央仓库,附详细使用说明)

前言:之前工作中做过两个功能,就是之前写的这两篇博客,最近几天有个想法,给它做成一个springboot的start启动器,直接引入依赖,写好配置就能用了 springboot使用自定义注解实现接口参数解密,普通…

4.5--计算机网络之基础篇--1.模型分层--(复习+深入)---好好沉淀,加油呀

1.TCP/IP模型的分层 1.1.为什么要有 TCP/IP 网络模型? 对于同一台设备上的进程间通信,有很多种方式,比如有管道、消息队列、共享内存、信号等方式; 而对于不同设备上的进程间通信,就需要网络通信,而设备是…

Elasticsearch:索引状态是红色还是黄色?为什么?

在我之前文章 “Elasticsearch:如何调试集群状态 - 定位错误信息” 中,我有详细介绍如何调试集群状态。在今天的文章中,我将详细介绍如何故障排除和修复索引状态。 Elasticsearch 是一个伟大而强大的系统,特别是创建一个可扩展性极…

51单片机-LED篇

目录准备工作点亮一个LED灯写程序烧录LED闪烁延时代码Delay500ms烧录LED流水灯代码对LED流水灯代码进行优化,增加复用性延时代码代码准备工作 使用到的单片机是普中51单片机 使用到的软件是Keil uVision5和stc-isp 点亮一个LED灯 写程序 首先通过Keil uVision5…

多个硬盘挂载到同一个目录

同一目录无法重复挂载,后挂载的会覆盖之前挂载的磁盘。但是现在需要将4块磁盘并行挂载,该如何操作呢? 将2块磁盘合并到一个逻辑卷 进行挂载。 基本知识 基本概念PV(Physical Volume)- 物理卷物理卷在逻辑卷管理中处于最底层,它可…

MyBatisPlus-DML编程控制

MyBatisPlus-DML编程控制4,DML编程控制4.1 id生成策略控制知识点1:TableId4.1.1 环境构建4.1.2 代码演示AUTO策略步骤1:设置生成策略为AUTO步骤2:删除测试数据并修改自增值步骤3:运行新增方法INPUT策略步骤1:设置生成策略为INPUT步骤2:添加数据手动设置I…

Muduo库源码剖析(八)——TcpServer类

TcpServer类 要点 TcpServer类的主要作用是,管理整个服务器,做如下的一些操作: 管理accept(2) 获得的 TcpConnectionTcpServer是供用户直接使用的,生命期由用户控制设置 mainLoop,并利用 mainLoop 进行新连接的管理初始化TcpSe…

腾讯云轻量应用服务器价格表(2023版)

2023腾讯云轻量应用服务器2核2G4M带宽88元一年、2核4G6M带宽159元/年、4核8G10M优惠价425元、8核16G14M价格1249、16核32G20M服务器2499元一年,今天分享2023腾讯云服务器配置及精准报价。 腾讯云轻量应用服务器优惠价格表 腾讯云服务器分为轻量应用服务器和云服务器…
最新文章