JAVA虚拟机字节码执行引擎之运行时栈帧结构

导读

本章主要讲解栈帧的体系结构及java字节码在栈帧中是如何执行的,相信了解过java虚拟机体系结构的码友应该都知道,执行程序的主战场在虚拟机栈中,java中的代码执行是通过方法的调用来实现的,方法的调用和接收正好是一个栈帧入栈和出栈的过程,因此可以浅显的理解java字节码就是在栈帧中被解析执行的,通过本章内容,你可以了解到栈帧的体系结构,及栈帧中每一个部件的作用。

一、虚拟机栈与栈帧体系结构

在讲解栈帧之前我们先来拓展点内容,.java文件是如何在虚拟机中能够被识别并执行的?
在这里插入图片描述
JVM本身也拥有自己定义的一套字节码指令集,这些指令与.class文件中的16进制值对应,这套指令还不是真正的机器指令,需要JVM解析成机器指令后才能运行。
虚拟机栈
说到虚拟机栈,想必大家都非常熟悉了,它是JVM体系结构中的重要部分,是线程私有内存区域,每当创建一个线程时都会在JVM内部划分出一块虚拟机栈供其使用,也就是说,JVM中可以有多个虚拟栈结构。
栈帧
栈帧是用于支持虚拟机进行防腐调用和方法执行的数据结构,它是虚拟机栈的元素,同时栈帧还分为更细的结构,其内部由局部变量表、操作数栈、动态链接、方法返回地址等结构组成。
在这里插入图片描述
一个方法的调用过程,都对应着一个栈帧在虚拟机栈中的入栈和出栈过程,栈帧中的局部变量表需要多大的内存以及多深的操作数栈在编译器已经确定了,并把这部分信息写到了方法表中的Code属性之中,在方法调用前JVM会根据这部分信息分配好内存,不会受到运行期变量的数据而影响其大小;一个方法的调用链可能会很长,当很多方法都处于执行状态,对于执行引擎来说,在向前线程的虚拟机栈中,只有位于栈顶的战争才是生效的,称之为当前栈帧,与之对应的方法称为当前方法,执行引擎中的操作指令只针对当前栈帧进行操作,下面通过一张图来认识栈帧的概念模型:
在这里插入图片描述
说明: 上图把JVM的运行时数据区一起画出来了,这是为了知识扩展,本章主要研究的还是栈帧,主要把注意力聚焦到栈帧这块区域即可。
栈帧各区域结构

  • 局部变量表
    槽:
    局部变量表是一组变量存储区域,用于存储方法参数和方法内部定义的局部变量,在java程序编译为Class文件时,一个方法所需要分配的局部变量表最大容量值已经写到了方法的Code属性的max_locals数据项中;局部变量表以槽(slot)为存储的最小单位,虚拟机没有明确指定一个slot的大小,一般情况下1slot=32bit,当然随着处理器和操作系统的不同,其表示大小可以不一样,这里为了研究方便,就认为1slot=32bit,1个槽都应该能存放一个boolean、byte、short、int、char、float、reference、returnAddress类型数据,对于占用64位的long、double来说其通过对齐补白的方式为其分配两个连续slot空间,在long和double的非原子协定中吧一下long和double的读写分割为了两次32位读写操作,因为栈空间是线程私有的,所以这里可以保证线程安全。
    索引:
    局部变量表中,每一个槽都有对应的索引(类比数组),索引默认从0开始编号,虚拟机就是通过索引来访问局部变量表的,例如,虚拟机要访问第n个局部变量,如果第n个变量是32位数据类型变量,直接返回索引为n的槽,如果第n个变量是64位的数据类型变量则会返回第n和n+1两个索引对应的槽(前提是n之前的变量都是32位类型才能这样计算)
    方法参数槽的分配细节:
    在栈中的方法执行时,虚拟机使用局部变量表完成参数值到参数变量列表的传递,如果执行的是普通方法,局部变量表的第0位索引默认接收的是this,其余的变量按照方法参数顺序往下排1 2 3 4…,方法参数需要的槽分配完毕后,随后分配方法体内其余变量的槽。
    槽重用:
    局部变量表中的槽可以重用,当PC程序计数器指针划过变量的作用域(大括号的范围{}),那么此时这个槽就可以复用,从而节省空间。
  • 操作数栈
    操作数栈也叫操作栈,其实就是一个栈数据结构,特点是后进先出,栈的最大深度,同样在编译器就已经确定,保存在Code的max_stacks数据项中,操作数栈主要用于方法中的计算及方法中调用另外一个方法时通过操作数栈来传递参数,举个加法的栗子:a=10,b=30 当执行引擎解析class文件,遇到iadd指令时(class文件中我们看到的是16进制,但是这些16进制表示的意思在JVM中通过定义一堆指令来表示,你可以JVM指令和那一堆16进制是一个东西的两种表现形式而已),a、b压栈—>执行iadd—>a、b出栈进行累加(cpu)—>结果入栈。
  • 动态链接
    每一个栈帧都包含一个指向运行时常量池中该栈帧对应的方法引用(读到这里应该知道,方法也是有引用地址的不然你让执行引擎怎么找到你的方法呢),持有这个引用的目的是为了支持方法调用的动态链接
  • 方法返回地址
    即方法返回指令,方法的退出其实就等于将当前栈帧出栈,因此退出时,可能执行的操作有:恢复上层方法的局部变量表和操作数栈,如果有返回值,将返回值压如调用者栈帧的操作数栈中,调整程序计数器的值以指向方法调用指令的下一条指令,其实线程执行的行号指示器即为程序计数器,程序计数器中保存了线程执行的行对应的地址信息,当程序计数器接收到方法返回地址后,说明方法执行完毕了,会将自己存储的地址指向吓一条指令。
  • 附加信息
    例如调试相关的信息,这里不会对这一块信息做过多的介绍。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值