我是靠谱客的博主 缓慢铃铛,最近开发中收集的这篇文章主要介绍Java编译原理--运行时栈帧结构 一、 概述,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

Java语言在刚刚诞生的时候提出过一句著名的口号“一次编写,到处运行”,这句话充分的表达了开发人员对于冲破平台界限的渴望,也解释了Java语言跟平台无关的设定。

 一、 概述

Java虚拟机规定了虚拟机执行字节码的概念模型,这个模型是各类虚拟机的外观结构,不同的虚拟机可以有不同的实现,但是从外部看起来它们都是统一的,输入的是二进制字节流,经过执行引擎处理之后,输出执行结果。当二进制字节流进入内存后,在执行过程中,类、类变量、字段等都已经在内存中初始化,那么在方法执行过程中,方法体各结构如何存储,方法调用时如何实现的呢?本文将讨论这些内容。

 1、 运行时的数据区

 Java虚拟机在执行过程中会把它所管理的内存区域分为不同的区域,,这些区域都有各自的用途、创建及销毁时间,有的区域随着虚拟机的启动而存在,有的区域则依赖用户线程的调用而建立和销毁,根据Java虚拟机规范,Java虚拟机运行时应该包括方法区、堆、虚拟机栈、本地方法栈和程序计数器区域,各区域如下图所示:

 

程序计数器是一块很小的区域,它是用来记录当前线程所执行的字节码的行号指示器,字节码执行时,虚拟机是通过改变程序计数器的值来寻找字节码指令的,程序计数器是线程的私有区域,每个线程都会有自己的程序计数器。

虚拟机栈跟程序计数器类似,也是线程私有的区域,它的生命周期跟线程一致。虚拟机栈描述的是方法执行时的内存模型每个方法执行时都会在虚拟机栈中生成一块空间称为栈帧,用于存储方法所需要的各种变量和数据,方法的调用过程就伴随着一个栈帧在本地方法栈中的入栈和出栈过程。

本地方法栈跟虚拟机栈作用相似,也是线程私有区域,用于方法调用期间生成一块地址以存储变量,所不同的是,虚拟机栈为Java方法(二进制字节码)服务,本地方法栈是为虚拟机用到的本地方法(native)服务。

堆是内存中最大的一块区域,并且堆是Java虚拟机中所有线程共享的,随着虚拟机的启动而创建,随着虚拟机的退出而销毁,大部分的对象实例都要在堆上分配,虚拟机规范规定,Java堆可以不是连续的物理空间,只需要在逻辑上连续即可。

方法区跟Java堆一样,也是线程共享的区域,它用于存储虚拟机加载的类、常量、静态变量、即时编译器编译后的代码等数据,常量池即在方法区存在。

 2、 栈帧

 栈帧是用于虚拟机方法调用和方法执行的数据区域,它是虚拟机栈的栈元素,栈帧中存储了方法的局部变量表、操作数栈、动态链接和方法返回地址等信息。一个线程调用的方法可能很多,但是每个线程在一个时间点,只能执行一个方法,也就是处于栈帧顶部的栈帧才是有效栈帧,称为当前栈帧,执行的方法称为当前方法。局部变量表的大小和操作数栈的深度在代码编译阶段已经确定,并且将数值写入了Code属性中,因此一个栈帧分配多少内存,不会受到运行期变量数据的影响,仅仅取决于具体的虚拟机实现。一个典型的栈帧结构如下图所示:

 

 3、 局部变量表

局部变量表是一组变量值存储空间,用于存储方法参数和方法内定义的局部变量,在Java程序编译为class文件时,局部变量表的最大容量存储在Code属性的max_locals数据项中。

局部变量表中最小的单位是slot(变量槽),虚拟机没有明确规定一个slot的大小,只是说了一个slot应该能存放一个boolean、byte、char、short、int、float、reference、returnAddress类型,这些类型都可以使用32位或者更小的空间存储,并且可以用slot存储long、double等64位的数据类型,只要在逻辑上看起来跟32位虚拟机一致就行。

Java虚拟机通过索引的方式访问局部变量表,索引位置从0开始至索引的最大位置,也即是slot的数量为-1。虽然局部变量表用于存储方法参数和方法内部的局部变量,但是方法参数和方法内部的局部变量加起来的数值并不一定等于局部变量表的大小,因为局部变量表的空间是可以复用的。局部变量表示意图如下图所示:

 4、 操作数栈

 操作数栈也称为操作栈,是一个后入先出的数据结构,同局部变量表一样,操作数栈的深度也是在编译阶段确定,并且存储在Code属性的max_stacks数据项中,操作数栈中的数据可以是任意类型的,32位类型的数据占据一个栈容量,64位类型的数据占据两个栈容量。
    操作数栈的使用过程包括以下几项:
    1)栈帧刚创建时,操作数栈是空的;
    2)虚拟机可以对操作数栈进行入栈操作,比如把局部变量表或方法参数中的数据入栈;
    3)虚拟机也可以对操作数栈进行出栈操作;
    4)向其他方法传递的参数,也存在操作数栈中;
    5)调用其他方法的返回结果,也存储在操作数栈中。

概念模型中,栈帧之间是相互隔离的,但是在具体的虚拟机实现中,栈帧之间都会进行部分重叠,这样在方法调用时,可以减少部分参数的复制和传递,加快执行过程。操作数栈示意图如下图所示:

 

5、动态链接 

 一个方法调用另外的方法,或者一个类使用另外一个类的成员变量时,都需要通过直接引用来调用。代码经过编译后,会形成字节码文件,字节码文件中存储的都是符号引用,执行引擎需要使用直接引用,符号引用需要转换为直接引用,一部分符号引用会在类加载或者第一次使用时进行解析,称为静态解析;另外一部分符号引用需要在代码执行时动态进行解析,称为动态链接。

 6、方法返回地址

 当一个方法执行结束后,有两种方式可以退出这个方法,第一种是遇到方法的返回指令,这时候会有返回值传递给方法的调用者,这种返回方式称为正常返回;第二种返回方式是异常返回,方法执行过程中遇到内部异常,或者在异常表中没有搜索到匹配的异常处理器,会导致方法退出,这种方式不会给方法调用者返回值。

无论何种方法退出,都需要返回到方法调用者的位置,程序才能继续执行,正常退出时,栈帧中会存放方法调用者的PC计数器的值,用以回复调用者的执行状态;异常退出时,需要通过异常处理表来确定,这时候栈帧中不存放异常信息。

 

 

最后

以上就是缓慢铃铛为你收集整理的Java编译原理--运行时栈帧结构 一、 概述的全部内容,希望文章能够帮你解决Java编译原理--运行时栈帧结构 一、 概述所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(61)

评论列表共有 0 条评论

立即
投稿
返回
顶部