StackOverflowError在程序栈空间耗尽时抛出,通常是深度递归导致。StackOverflowError继承了VirtualMachineError类,后者表示JVM已被破坏或资源耗尽。更进一步,VirtualMachineError继承自Error类,应用程序不应该捕获这种严重的错误。不要再throw语句里面抛出这样的错误,因为这些错误是不应该发生的异常条件。StackOverflowError从Java 1.0版本就已存在。
StackOverflowError构造函数
StackOverflowError()
和StackOverflowError(String s)
,后者的String参数指明了抛出错误的类名。
The StackOverflowError in Java
当一个函数被Java程序调用的时候,就会在调用栈上分配栈帧。栈帧包含被调用函数的参数、局部变量和返回地址。返回地址指示了当函数执行完毕之后下一步该执行哪里。如果创建栈帧时没有内存空间,JVM就会抛出StackOverflowError。
最常见的耗尽Java栈的案例是递归。在递归操作中,函数执行时会调用自己。使用递归要小心,以免抛出StackOverflowError错误。如下的例子演示了递归如何抛出StackOverflowError:
public class StackOverflowErrorExample {
public static void recursivePrint(int num) {
System.out.println("Number: " + num);
if(num == 0)
return;
else
recursivePrint(++num);
}
public static void main(String[] args) {
StackOverflowErrorExample.recursivePrint(1);
}
}
如果num为0,递归就会终止,但是这里一开始传入1,每次递归都自增1,递归永远不会终止。
使用-Xss1M
参数指定线程栈空间大小为1M,这个例子的执行结果如下:
Number: 1
Number: 2
Number: 3
...
Number: 6262
Number: 6263
Number: 6264
Number: 6265
Number: 6266
Exception in thread "main" java.lang.StackOverflowError
at java.io.PrintStream.write(PrintStream.java:480)
at sun.nio.cs.StreamEncoder.writeBytes(StreamEncoder.java:221)
at sun.nio.cs.StreamEncoder.implFlushBuffer(StreamEncoder.java:291)
at sun.nio.cs.StreamEncoder.flushBuffer(StreamEncoder.java:104)
at java.io.OutputStreamWriter.flushBuffer(OutputStreamWriter.java:185)
at java.io.PrintStream.write(PrintStream.java:527)
at java.io.PrintStream.print(PrintStream.java:669)
at java.io.PrintStream.println(PrintStream.java:806)
at StackOverflowErrorExample.recursivePrint(StackOverflowErrorExample.java:4)
at StackOverflowErrorExample.recursivePrint(StackOverflowErrorExample.java:9)
at StackOverflowErrorExample.recursivePrint(StackOverflowErrorExample.java:9)
at StackOverflowErrorExample.recursivePrint(StackOverflowErrorExample.java:9)
...
More about the StackOverflowError in Java
下面的例子演示了类之间有循环关系时的风险。
class A {
private int aValue;
private B bInstance = null;
public A() {
aValue = 0;
bInstance = new B();
}
@Override
public String toString() {
return "";
}
}
class B {
private int bValue;
private A aInstance = null;
public B() {
bValue = 10;
aInstance = new A();
}
@Override
public String toString() {
return "";
}
}
public class StackOverflowErrorToStringExample {
public static void main(String[] args) {
A obj = new A();
System.out.println(obj.toString());
}
}
当创建A对象的时候需要创建B对象,创建B对象的时候又要创建A对象,在两个类之间形成了循环依赖,最终导致StackOverflowError。输出结果:
Exception in thread "main" java.lang.StackOverflowError
at main.java.B.(StackOverflowErrorToStringExample.java:24)
at main.java.A.(StackOverflowErrorToStringExample.java:9)
at main.java.B.(StackOverflowErrorToStringExample.java:24)
at main.java.A.(StackOverflowErrorToStringExample.java:9)
at main.java.B.(StackOverflowErrorToStringExample.java:24)
at main.java.A.(StackOverflowErrorToStringExample.java:9)
...
如何处理StackOverflowError
- 最简单的方法就是细致的检查stack trace,找出行号的重复模式。这些重复的行号代表了被递归调用的代码。仔细审查代码,理解为何递归不终止。
- 如果你确认递归实现没有问题,你可以通过-Xss参数增加栈的大小,这个参数可以在项目配置或命令行指定。