Java 虚拟机栈详解分析
Java虚拟机栈
1. 定义
- 栈:线程运行时需要的内存空间,一个栈存在多个栈帧。栈具有先入后出,后入先出的特点。
- 栈帧:每个方法运行时需要的内存(局部变量表、操作数栈、动态链接和方法返回值等信息。),每次调用一个方法,便会将栈帧压入栈中,方法执行完毕将栈帧从栈顶压出
- 活动栈帧:指在栈顶的栈帧,既正在调用的方法,每个线程只能有一个活动栈帧,对应着该线程正在调用的那个方法
现在我们用代码来演示一下Java虚拟机如何将栈帧压入及压出栈中
public class Main { public static void main(String[] args) { method1(); } private static void method1() { method2(1, 2); } private static int method2(int a, int b) { int c = a + b; return c; } }
当我们运行Main函数时,jvm首先将栈帧Main压入栈中,此视栈结构如图所示
Main函数体中调用了method1方法,此时便会将栈帧1压入栈中
method1方法体中调用了method2方法,这时jvm会将栈帧2压入栈结构中,需要注意的是,前面我们提到了栈帧由 “局部变量表、操作数栈、动态链接和方法返回值”等信息组成,method2方法中拥有a,b两个参数以及局部变量c和方法返回值
当method2方法执行完后,会将栈帧2从栈顶弹出
method1方法执行完毕后,将栈帧1弹出
依次顺序直至线程被销毁。
注意点
- 由于每个栈帧都会在方法调用完毕后被弹出,因此栈内存不需要进行垃圾回收
- 每个栈都是线程私有的,每个线程在创建的时候都会创建一个虚拟机栈,而由于物理内存是固定的,栈内存划分得越大,可分配的线程数就越少
2. 栈的线程安全问题
局部变量是线程安全的
现在我们定义如下方法:
public static void method() { int a = 0; a++; }
我们在方法method中定义了一个局部变量a,并对其执行a++操作,现在假设我们有两个线程同时调用了这个方法(栈帧),Java虚拟机会将该栈帧压入各自线程的栈内存中,但由于局部变量表是线程私有的,所以两个线程在同时调用这个栈帧后,a的值仍然都为1,故局部变量是线程安全的
方法参数和方法返回值不是线程安全
由于方法的参数和返回值均可被外部方法所引用,故在某个线程下某个方法可以更改另外一个方法的参数和返回值,故方法参数和方法返回值不是线程安全的
3. 栈内存溢出
由于程序执行时,虚拟机给每个栈分配的栈内存空间是固定的,所以在一些情况下有可能出现栈内存空间不足,导致溢出的情况,一般有两种情况可能导致栈内存溢出
- 栈帧过大(较少出现)
- 栈帧过多(一般出现在递归时。没有正确设置递归出口)
现在我们来解释一下什么时候会出现栈帧过多导致栈内存溢出,我们来假设某一个栈的内存空间大小是1024kb,现在有四个栈帧,每个栈帧的大小均为300kb,而 300 * 4 = 1200kb, 而 1200 > 1024 ,很显然,现在这四个栈帧合起来的大小已经超过了这个栈的内存空间大小,这个时候就会出现栈内存溢出,也就是会报java.lang.StackOverflowError这个错误。
到此这篇关于Java 虚拟机栈详解分析的文章就介绍到这了,更多相关Java 虚拟机栈内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!