Java开发中,每当咱们在程序中使用new生成一个对象,对象的引用存放在栈里,而对象是存放在堆里的。能够看出栈在Java核心的重要位置。今天咱们就继续深刻Java核心这个系列,为您介绍Java中的栈、局部变量及其之间的关系。数组
深刻Java核心:Java内存分配原理精讲 探秘Java垃圾回收机制 Java中多态的实现机制 多线程
Java中的栈this
每当启用一个线程时,JVM就为他分配一个Java栈,栈是以帧为单位保存当前线程的运行状态。某个线程正在执行的方法称为当前方法,当前方法使用的栈帧称为当前帧,当前方法所属的类称为当前类,当前类的常量池称为当前常量池。当线程执行一个方法时,它会跟踪当前常量池。spa
每当线程调用一个Java方法时,JVM就会在该线程对应的栈中压入一个帧,这个帧天然就成了当前帧。当执行这个方法时,它使用这个帧来存储参数、局部变量、中间运算结果等等。线程
Java栈上的全部数据都是私有的。任何线程都不能访问另外一个线程的栈数据。因此咱们不用考虑多线程状况下栈数据访问同步的状况。指针
像方法区和堆同样,Java栈和帧在内存中也没必要是连续的,帧能够分布在连续的栈里,也能够分布在堆里code
Java栈的组成元素——栈帧htm
栈帧由三部分组成:局部变量区、操做数栈、帧数据区。局部变量区和操做数栈的大小要视对应的方法而定,他们是按字长计算的。但调用一个方法时,它从类型信息中获得此方法局部变量区和操做数栈大小,并据此分配栈内存,而后压入Java栈。对象
局部变量区 局部变量区被组织为以一个字长为单位、从0开始计数的数组,类型为short、byte和char的值在存入数组前要被转换成int值,而long和double在数组中占据连续的两项,在访问局部变量中的long或double时,只需取出连续两项的第一项的索引值便可,如某个long值在局部变量区中占据的索引时三、4项,取值时,指令只需取索引为3的long值便可。blog
下面就看个例子,好让你们对局部变量区有更深入的认识。这个图来自《深刻JVM》:
- public static int runClassMethod(int i,long l,float f,double d,Object o,byte b) {
- return ;
- }
- public int runInstanceMethod(char c,double d,short s,boolean b) {
- return ;
- }
上面代码片的方法参数和局部变量在局部变量区中的存储结构以下图:
上面这个图没什么好说的,你们看看就会懂。可是,在这个图里,有一点须要注意:
runInstanceMethod的局部变量区第一项是个reference(引用),它指定的就是对象自己的引用,也就是咱们经常使用的this,可是在runClassMethod方法中,没这个引用,那是由于runClassMethod是个静态方法。
操做数栈和局部变量区同样,操做数栈也被组织成一个以字长为单位的数组。但和前者不一样的是,它不是经过索引来访问的,而是经过入栈和出栈来访问的。可把操做数栈理解为存储计算时,临时数据的存储区域。下面咱们经过一段简短的程序片断外加一幅图片来了解下操做数栈的做用。
int a = 100;
int b = 98;
int c = a+b;
从图中能够得出:操做数栈其实就是个临时数据存储区域,它是经过入栈和出栈来进行操做的。
帧数据区除了局部变量区和操做数栈外,Java栈帧还须要一些数据来支持常量池解析、正常方法返回以及异常派发机制。这些数据都保存在Java栈帧的帧数据区中。
当JVM执行到须要常量池数据的指令时,它都会经过帧数据区中指向常量池的指针来访问它。
除了处理常量池解析外,帧里的数据还要处理Java方法的正常结束和异常终止。若是是经过return正常结束,则当前栈帧从Java栈中弹出,恢复发起调用的方法的栈。若是方法又返回值,JVM会把返回值压入到发起调用方法的操做数栈。
为了处理Java方法中的异常状况,帧数据区还必须保存一个对此方法异常引用表的引用。当异常抛出时,JVM给catch块中的代码。若是没发现,方法当即终止,而后JVM用帧区数据的信息恢复发起调用的方法的帧。而后再发起调用方法的上下文从新抛出一样的异常。
栈的整个结构
在前面就描述过:栈是由栈帧组成,每当线程调用一个Java方法时,JVM就会在该线程对应的栈中压入一个帧,而帧是由局部变量区、操做数栈和帧数据区组成。那在一个代码块中,栈究竟是什么形式呢?下面是我从《深刻JVM》中摘抄的一个例子,你们能够看看:
代码片断:
执行过程当中的三个快照:
上面所给的图,只想说明两件事情,咱们也可用此来理解Java中的栈:
一、只有在调用一个方法时,才为当前栈分配一个帧,而后将该帧压入栈。
二、帧中存储了对应方法的局部数据,方法执行完,对应的帧则从栈中弹出,并把返回结果存储在调用方法的帧的操做数栈中。