浅谈java.lang.StackOverflowError
StackOverflowError:栈溢出错误,如果一个线程所需用到栈的大小>配置允许最大的栈大小,那么jvm就会抛出StackOverflow。查看一下程序可能是有死循环或递归调用所产生的,也可以增大JVM的内存
栈的特点
1、栈,也叫栈内存,是jvm的内存模型之一,每当启动一个新线程的时候,jvm都会为它分配一个java栈。jvm只会直接对java栈执行两种操作,以帧为单位的压栈和出栈。
2、栈存储的内容:方法内的局部变量表、操作数、动态链接、方法出口信息、其他等信息。
- 1)局部变量表:保存函数的参数以及局部变量用的,局部变量表中的变量只在当前函数调用中有效,当函数调用结束后,随着函数栈帧的销毁,局部变量表也会随之销毁。
- 2)操作数:主要用于保存计算过程的中间结果,同时作为计算过程中变量临时的存储空间。在概念模型中,两个栈帧是相互独立的,但是大多数虚拟机的实现都会进行优化,令两个栈帧出现一部分重叠,令下面部分的操作数栈与上面部分的局部变量表重叠在一块,这样在方法调用的时候可以共用一部分数据,无需进行额外的参数复制传递。
- 3)动态链接:每个栈帧都包含一个指向运行时常量池中该栈帧所属方法的引用,持有这个引用是为了支持方法调用过程中的动态连接。在Class文件的常量池中存有大量的 符号引用,字节码中的方法调用指令就以常量池中指向方法的符号引用为参数。这些符号引用一部分会在类加载阶段或第一次使用的时候转化为直接引用,这种转化 称为静态解析。另外一部分将在每一次的运行期期间转化为直接引用,这部分称为动态连接。
- 4)方法出口信息:在方法退出之前,都需要返回到方法被调用的位置,程序才能继续执行,方法返回时可能需要在栈帧中保存一些信息,用来恢复它的上层方法的执行状态,方法出口信息获取分为正常退出和异常退出。正常退出通过pc计数器的值获取,异常退出通过异常处理器表确定返回地址。
- 5)附加信息:虚拟机规范中允许具体的虚拟机实现增加一些规范中没有描述的信息到栈帧中,这部分信息取决于虚拟机的实现。
3、栈的生命周期:随着线程的创建而创建,线程的结束而消亡,释放内存,所以栈内存是私有的
4、栈的存储方式:栈内存以栈帧(Stack Frame)为单位存储,栈帧是一个内存区块,是一个有关方法和运行期数据的数据集。当一个方法M1被调用的时候,就会产生一个栈帧S1,并被压入到栈中,M1方法又调用了M2方法,这个时候又产生栈帧S2也被压入栈,M2方法执行完毕后,S2栈帧先出栈,S1栈帧再出栈,遵循“先进后出”原则。
出现StackOverflowError的原因分析
一般出现这个问题是因为程序里有死循环或递归调用所产生的。
如:
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//死循环
int i=0;
while (true){
i++;
Log.i("ruxing","i="+i);
}
}
//递归
private void add(int i){
i++;
Log.i("ruxing","i="+i);
add(i);
}
}
以递归为例,详解程序:
1)启动MainActivity,会创建一个线程,同时创建一个栈内存。
2)调用add()方法的时候,会对add()方法进行压栈操作,将add()运行期数据的数据集保存到栈帧中。
3)add()递归调用时,都会产生一个新的栈帧区块,这是就会连续的产生新的栈帧区块。
4)当栈内存超过系统配置的栈内存,就会出现java.lang.StackOverflowError异常。