周志明先生所著的《深入理解Java虚拟机:JVM高级特性与最佳实践》(购买地址:亚马逊链接),对我学习Java、理解Java之道有非常大的帮助。至今已读过两遍,为了能够融会贯通,加深记忆(人老了记忆力差),便在Blog上记录一些认为该记的东西。
栈帧(Stack Frame)是用于支持虚拟机进行方法调用和方法执行的数据结构,它是虚拟机运行时数据区中的虚拟机栈的栈元素。栈帧存储了方法的局部变量表、操作数栈、动态连接和方法返回地址等信息。每一个方法从调用开始到执行完成的过程,就对应着一个栈帧在虚拟机栈里面从入栈到出栈的过程。
在编译程序代码的时候,栈帧中需要多大的局部变量表、多深的操作数栈都已经完全确定了,并且写入到方法表的Code属性之中,因此一个栈帧需要分配多少内存,不会受到程序运行期变量数据的影响,而仅仅取决于具体的虚拟机实现。
对于执行引擎来说,活动线程中,只有栈顶的栈帧是有效的,称为当前栈帧(Current Stack Frame),这个栈帧所关联的方法称为当前方法(Current Method)。执行引擎所运行的所有字节码指令都只针对当前栈帧进行操作,栈帧的概念结构如下图:
局部变量表
局部变量表是一组变量值存储空间,用于存放方法参数和方法内部定义的局部变量。在Java程序被编译为class文件时,就在方法的Code属性的max_locals数据项中确定了该方法所需要分配的最大局部变量表的容量。
局部变量表的容量以变量槽(Variable Slot)为最小单位。虚拟机规范中允许Slot的长度随着处理器、操作系统或虚拟机的不同而发生变化。在32位系统中,一个Slot可以存放一个32位以内的数据类型。Java中占用32位以内的数据类型有boolean,byte,char,short,int,float,reference和returnAddress八种。Reference是对象的引用,虚拟机实现至少都应该能从此引用中直接或间接地查找到对象在Java堆中的其实地址索引和方法区中的对象类型数据。returnAddress指向了一条字节码指令的地址。
对于64位的数据类型(double和long),虚拟机会以高位在前的方式为其分配两个连续的Slot空间。
虚拟机通过索引定位的方式使用局部变量表,索引值的范围是从0开始到局部变量表最大的Slot数据。如果是实例方法(非static的方法),那么局部变量表中第0位索引的Slot默认是用于传递方法所属对象实例的引用(关键字this)。其余参数按照参数表的顺序来排序,占用从1开始的局部变量Slot。参数表分配完毕后,再根据方法体内部定义的变量顺序和作用域分配其余Slot。
操作数栈
操作数栈也常被成为操作栈,它是一个LIFO栈。操作数栈的最大深度也在编译的时候被写入到Code属性的max_stacks数据项之中。32位数据类型所占用的栈容量为1 ,64位数据类型为2 。
当一个方法刚刚开始执行的时候,这个方法的操作数栈是空的,在方法的执行过程中,会有各种字节码指令向操作数栈中写入和提取内容,也就是入栈出栈操作。比如,整数加法的字节码指令iadd在运行的时候要求操作数栈中最接近栈顶的两个元素已经存入了两个int型的数值,当执行这个指令是,会将这两个int值出栈并相加,然后将相加的结果入栈。
Java虚拟机的解释执行引擎称为“基于栈的执行引擎”,其中所值的“栈”就是操作数栈。
动态连接
每个栈帧都包含一个指向运行时常量池中该栈帧所属方法的引用,持有这个引用是为了支持方法调用过程中的动态连接。Class文件的常量池中存有大量的符号引用,字节码中的方法调用指令就以常量池中指向方法的符号引用为参数。这些符号引用一部分会在类加载阶段或第一次使用的时候转化为直接引用,这种转化称为静态解析。另外一部分将在每一次的运行期间转化为直接引用,这部分称为动态连接。
方法返回地址
当一个方法被执行后,有两种方式退出这个方法。第一种是执行引擎遇到任意一个方法返回的字节码指令,这时候可能会有返回值传递给上层的方法调用者,是否有返回值和返回值的类型将根据遇到何种方法返回指令来决定,这种退出方法的方式称为正常完成出口(Normal Method Invocation Completion)。
另一种退出方式是,在方法执行过程中遇到了异常,并且这个异常没有在方法体内得到处理,无论是JVM内部产生的异常,还是用户代码中使用athrow字节码指令产生的异常,只要是本方法的异常表中没有搜索到匹配的异常处理器,就会导致方法退出,这种退出方法的方式成为异常完成出口(Abrupt Method Invocation Completion),此时不会给它的上层调用者产生任何返回值。
无论采用何种退出方式,在方法退出后,都需要返回到方法被调用的位置。方法返回时可能需要在栈帧中保存一些信息,用来帮助恢复它的上层方法的执行状态。
方法退出的过程实际上等同于把当前栈帧出栈,因此退出时可能执行的操作有:恢复上层方法的局部变量表和操作数栈,把返回值(如果有的话)压入调用者栈帧的操作数栈中,调整PC计数器的值以指向方法调用指令后面的一条指令等。
相关推荐
在不同的虚拟机实现里,执行引擎在执行 java 代码的时候,可能会解释执行和编译执行等,但是从外观上来看,所有的 java 虚拟机的执行引擎都是一致的:输入的是字节码文件,处理过程是字节码解析的等效过程,输出的...
基于虚拟机字节码注入的Android应用程序隐私保护机制.pdf
第95节运行时栈帧结构00:08:46分钟 | 第96节局部变量表00:20:48分钟 | 第97节操作数栈00:08:36分钟 | 第98节动态连接00:02:56分钟 | 第99节方法返回地址和附加信息00:03:24分钟 | 第100节方法调用-解析调用00:...
第95讲 运行时栈帧结构 00:08:46 第96讲 局部变量表 00:20:48 第97讲 操作数栈 00:08:36 第98讲 动态连接 00:02:56 第99讲 方法返回地址和附加信息 00:03:24 第100讲 方法调用-解析调用 00:09:49 ...
深入Java虚拟机JVM类加载学习笔记:jvm java classloader 垃圾回收 gc
详细介绍ASM框架的API、Class文件结构解析、HotSpot虚拟机类加载源码分析、动态代理与字节码插桩的实现。
基于虚拟机字节码注入的Android应用程序隐私保护机制.docx
使用虚拟机的基本步骤如下: 选择合适的虚拟机软件。市场上有多种虚拟机软件可供选择,如VMware、VirtualBox、Hyper-V等,每种软件都有其特点和优势,用户可以根据自己的需求和硬件环境选择合适的软件
Android虚拟机ART
该文档是本人的学习笔记,尚未精修,后续会精修排版. 学习笔记:深入浅出 Java 虚拟机.docx
Java 虚拟机学习笔记: Java 内存区域, 垃圾收集, 内存分配与回收策略, JVM 调优, 文件结构, 类加载机制, Java 程序 Java是一种面向对象的编程语言,由Sun Microsystems于1995年推出。它是一种跨平台的语言,...
graalvm是一个通用的虚拟机,用于运行用javascript、python、ruby、r、基于jvm的语言(如java、scala、clojure、kotlin)和基于llvm的语言(如c和c++)编写的应用程序。
Java虚拟机(字节码介绍)1
Java虚拟机(JVM)是Java Virtual Machine的缩写,...类加载器负责将字节码文件加载到内存中,运行时数据区用于存储程序执行时所需的数据,执行引擎则负责执行字节码文件,而垃圾收集器则负责回收不再使用的内存空间。
将以太坊虚拟机字节码编译为已编译编程语言(如Java),WIP的源代码。 安装 通过Node上的npm: npm install evm2code 用法 待定 参考 待定 样本 待定 版本号 待定 贡献 随时并提交-欢迎捐款。 如果您提交拉取请求...
第三部分分析了虚拟机的执行子系统,包括类文件结构、虚拟机类加载机制、虚拟机字节码执行引擎。第四部分讲解了程序的编译与代码的优化,阐述了泛型、自动装箱拆箱、条件编译等语法糖的原理;讲解了虚拟机的热点探测...
不需要对虚拟机中内核代码进行修改.top: 获得某虚拟机CPU利用率free: 获得某虚拟机内存利用率虚拟机系统调用表保护功能ps: 列出某虚拟机中所有进程信息lsmod: 列出某虚拟机中所有模块(驱动)信息translate: 将某...
重装VMware虚拟机后,在虚拟机上运行ubuntu和Debian可能出现的2种无法打开的解决方法 出现情况:1、Mount of filesystem failed;2、the root filesystem is currently mounted readonly
C程序学习第一天学习笔记:使用虚拟机基础编译,进制转换,注意事项。