每当启动一个新线程时,Java虚拟机都会为它分配一个Java栈。Java栈以帧为单位保存线程的运行状态。虚拟机只会直接对Java栈执行两种操作:以帧为单位的压栈和出栈。
某个线程正在执行的方法被称为该线程的当前方法,当前方法使用的栈帧称为当前帧,当前方法所属的类称为当前类,当前类的常量池称为当前常量池。在线程执行一个方法时,它会跟踪当前类和当前常量池。此外,当虚拟机遇到栈内操作指令时,它对当前帧内数据执行操作。
每当线程调用一个Java方法时,虚拟机都会在该线程的Java栈中压入一个新帧。而这个新帧自然就成为了当前帧。在执行这个方法时,它使用这个帧来存储参数、局部变量、中间运算结果等数据。
Java方法可以以两种方式完成。一种通过return返回的,称为正常返回;一种是通过抛出异常而异常终止的。不管以哪种方式返回,虚拟机都会将当前帧弹出Java栈然后释放掉,这样上一个方法的帧就成为当前帧了。
Java帧上的所有数据都是此线程私有的。任何线程都不能访问另一个线程的栈数据,因此我们不需要考虑多线程情况下栈数据的访问同步问题。当一个线程调用一个方法时,方法的的局部变量保存在调用线程Java栈的帧中。只有一个线程能总是访问那些局部变量,即调用方法的线程。
JVM栈之局部变量表:包含参数和局部变量
局部变量表存放了基本数据类型、对象引用和returnAddress类型(指向一条字节码指令的地址)。其中64位长度的long和double类型的数据会占用2个局部变量空间(slot)(下图1到3的原因),其余数据类型只占用1个。局部变量表所需的内存空间在编译期间完成分配。每个方法都对应一个栈帧。
public class StackDemo {</span><span style="color: #008000;">//</span><span style="color: #008000;">静态方法</span> <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">static</span> <span style="color: #0000ff;">int</span> runStatic(<span style="color: #0000ff;">int</span> i, <span style="color: #0000ff;">long</span> l, <span style="color: #0000ff;">float</span> f, Object o, <span style="color: #0000ff;">byte</span><span style="color: #000000;"> b) { </span><span style="color: #0000ff;">return</span> 0<span style="color: #000000;">; } </span><span style="color: #008000;">//</span><span style="color: #008000;">实例方法</span> <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">int</span> runInstance(<span style="color: #0000ff;">char</span> c, <span style="color: #0000ff;">short</span> s, <span style="color: #0000ff;">boolean</span><span style="color: #000000;"> b) { </span><span style="color: #0000ff;">return</span> 0<span style="color: #000000;">; }