JVM 中的 returnAddress过程详解
目录
- 数据类型
- 栈帧
JVM是虚拟机,也是一种规范,他遵循着冯·诺依曼体系结构的设计原理。冯·诺依曼体系结构中,指出计算机处理的数据和指令都是二进制数,采用存储 程序方式不加区分的存储在同一个存储器里,并且顺序执行,指令由操作码和地址码组成,操作码决定了操作类型和所操作的数的数字类型,地址码则指出地址码和 操作数。从dos到window8,从unix到ubuntu和CentOS,还有MAC OS等等,不同的操作系统指令集以及数据结构都有着差异,而JVM通过在操作系统上建立虚拟机,自己定义出来的一套统一的数据结构和操作指令,把同一套语 言翻译给各大主流的操作系统,实现了跨平台运行,可以说JVM是java的核心,是java可以一次编译到处运行的本质所在。
参考文档:jvms12
数据类型
在 JVM 中,数据分为两大类:primitive types (原生类型)和 reference types(引用类型)。
引用类型,让 JVM 能更好的支持于面向对象语言的设计,引用类型的值用来指向内存中分配的类实例或者数组。JVM 规范中并没有详细规定引用类型的实现细节,比如引用应该通过何种方式去定位、访问堆中的对象,具体的对象访问方式取决于虚拟机的具体实现,比如 HotSpot 有其自己的实现方案。
目前主流的访问方式有使用句柄和直接指针两种:
其中使用直接指针访问的方式,类似于 C++ 中的虚表(虚表就是指向对象类型数据的指针)。这两种对象访问方式各有优劣,使用句柄访问的最大好处就是 reference 中存储的是稳定的句柄地址,在对象被移动(比如垃圾回收时,整理内存空间,会移动对象的存储位置)时只会改变句柄中示例数据的指针,而 reference 本身不需要修改。
使用直接指针访问的最大好处就是速度更快,节省了一次内存寻址的时间开销。
原生数据类型包括:numeric types, boolean type, returnAddress type。其中 returnAddress 数据只存在于字节码层面,与编程语言无关,也就是说,我们在 Java 语言中是不会直接与 returnAddress 类型的数据打交道的。
returnAddress 类型的值是指向字节码的指针,不管是物理机还是虚拟机,运行时内存中的数据总归可分为两类:代码,数据。对于冯诺依曼结构的计算机,指令数据和数值数据都存储在内存中,而哈弗结构的计算机,将程序指令与数据分开存储。
对于 JVM 来说,程序就是存储在方法区的字节码指令,而 returnAddress 类型的值就是指向特定指令内存地址的指针。
JVM支持多线程,每个线程有自己的程序计数器(pc register),而 pc 中的值就是当前指令所在的内存地址,即 returnAddress 类型的数据,当线程执行 native 方法时,pc 中的值为 undefined。
栈帧
栈帧(Stack Frame)是用于支持虚拟机进行方法调用和方法执行的数据结构,栈帧中存储了方法的局部变量表,操作数栈,动态连接,和方法返回地址等信息。在程序编译时,栈帧中需要多大的局部变量表,多深的操作数栈都已经完全确定了,并且写在方法表的 Code 属性中。
当一个方法开始执行后,只有两种方式可以退出,第一种方式是执行引擎遇到任意一个方法返回的字节码指令,这种方式称为正常完成出口;另外一种退出方式是,在方法执行过程中遇到异常,且该异常没有被被捕获,称为异常完成出口。
无论是哪种退出方式,在方法退出后,都需要返回到该方法被调用的位置(地址),让程序继续执行。一般来说,方法执行前,会保存调用者当前的 PC 计数器中的值,当方法正常退出时,将该 PC 计数器的值会作为返回地址,返回给调用者。在方法异常退出时,返回地址是通过异常处理器表来确定的。
方法退出的过程实际上就等于把当前栈帧出栈,一般过程为:
- 恢复上层方法的局部变量表和操作数栈
- 把返回值压入调用者栈帧的操作数栈中
- 调整 PC 计数器的值,以指向方法调用指令后面的一条指令
到此这篇关于JVM 中的 returnAddress的文章就介绍到这了,更多相关JVM returnAddress内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!