JVM 虚拟机栈详解

来源:互联网 发布:win10 php自定义检错 编辑:程序博客网 时间:2024/06/06 20:52

当Java虚拟机运行程序时。每当一个新的线程被创建时。Java 虚拟机都会分配一个虚拟机栈,Java虚拟机栈是以帧为单位来保存线程的运行状态。Java栈只会有两种操作:以帧为单位进行压栈跟出栈。
某个线程正在执行的方法称为当前方法,以此类推出当前类,当前常量池(每一个方法都有自己唯一的常量池)
每当线程调用当前方法时,都会将,新栈压入,成为当前帧。jvm会使用它来存储我们的形参,局部变量,中间运行结果等。
执行结束返回的方式会有两种,一种是 retuen正常返回,另外一种是通过异常来返回。无论哪种方式虚拟机都会释放弹出当前帧。这样上一个方法就成为了当前帧
之前我们提过Java栈中的数据都是线程私有的,因此线程都不能访问另一个线程的数据。因此我们在此无需考虑多线程。
栈帧
栈帧由,局部变量表,操作数栈,帧数据区组成。

class Examplea3a{public static int runClassMethod(int i,long l,float f,double d,Object o, byte b){   return 1;    };public int runInstanceMethod(char c,double d,short s,boolean b){  return 0;   };    }

这里写图片描述

上图描述了 Examplea3a 类中两个方法的局部变量区, 值得俺们注意的是在runInstanceMethod方法中,索引为0的参数是一个引用类型的reference,尽管方法中没有显示的声明这个参数,但这个this是任何一个实例方法都存在的,而类方法中就没有这个this变量,类方法只与类的声明中欧琪相关,无法关联到一个具体的实例。

操作数栈 与局部方法区一样,也是一个数组,不同的是操作数栈不是通过索引来访问的。而是通过标准的栈操作:进栈–出栈来操作来访问的。
虚拟机把操作数栈作为工作空间,执行运算,最后把结果再次压入到操作数栈中。

  iload_0   // push the int local variable 0    iload_1   // push the int local variable 1      iadd   // pop two ints, add them, push results        istore_2   // pop two ints ,store into local variable 

在这个字节码序列中,iload_0 , iload_1 将存储在局部变量表中索引为0,1的数压入到操作数栈中,之后执行iadd,再将结果压入到操作数栈。第四条指令则从操作数栈弹出,并将其存储到局部变量表中索引为2的位置。如下图
这里写图片描述

除了以上局部变量跟操作数栈以外,Java栈还需要一些数据来支持我们常量池的解析,方法返回,以及异常派发机制。这些新信息都存储在帧数据区。 当虚拟机执行某个命令时,需要常量池的数据池的指令。就会通过帧数据区指向常量池的指针来访问他,常量池中对类型,字段,方法的入口起始时都是符号一用,当虚拟机要在常量池中进行搜索时,如果指导类,接口,或方法的入口。若他么恩还是符号类型,虚拟机才会进行解析。 Java栈可能的实现方式(一):
class A(){ private static void addAndPrint(){ double result  = addTwoTypes(1,55.55)    }private static double addTwoTypes(int i ,double d){   return i+d;    }}

内存使用

当执行addAndPrint()方法,为了调用addTwoTypes(),addAndPrint()首先将 1,55.55压入到自己的操作数栈堆中。然后调用addTwoTypes(),进行常量池解析
注意addAndPrint()调用addTwoTypes()方法的时候也是需要解析常量池才可以使用该方法的。尽管他俩属于一个类。但类中的方法,字段初始也是符号引用,使用之前也要解析。
解析后的常量池将解析后的数据指向addTwoTypes(),虚拟机将根据这些信息来决定局部变量,和操作数栈的数据区的大小。虚拟机在堆中分配好足够的内存后,ddAndPrint()弹出他的栈帧,再将1,55.55压入到addTwoTypes()的局部变量表0,1索引的位置,在通过栈操作数操作完,返回后找到addAndPrint()调用方释放,最够把ddTwoTypes()作为当前帧,继续执行addAndPrint()。

Java栈可能的实现方式(二):
另外一种栈的实现方式是将栈操作数和局部变量表存储在连续的虚拟机栈中,

这里写图片描述
(栈是向下生长的)
addTwoTypes()与 addAndPrint()方法的额栈帧区是连续的,在调用addTwoTypes()方法时。我们将addAndPrint()操作数栈数据区,作为addTwoTypes()局部变量空间。这样贼省空间,又省时间!