StackOverflowError在程序??臻g耗盡時拋出,通常是深度遞歸導致。StackOverflowError繼承了VirtualMachineError類,后者表示JVM已被破壞或資源耗盡。更進一步,VirtualMachineError繼承自Error類,應用程序不應該捕獲這種嚴重的錯誤。不要再throw語句里面拋出這樣的錯誤,因為這些錯誤是不應該發(fā)生的異常條件。StackOverflowError從Java 1.0版本就已存在。
StackOverflowError構(gòu)造函數(shù)
StackOverflowError()和StackOverflowError(String s),后者的String參數(shù)指明了拋出錯誤的類名。
The StackOverflowError in Java
當一個函數(shù)被Java程序調(diào)用的時候,就會在調(diào)用棧上分配棧幀。棧幀包含被調(diào)用函數(shù)的參數(shù)、局部變量和返回地址。返回地址指示了當函數(shù)執(zhí)行完畢之后下一步該執(zhí)行哪里。如果創(chuàng)建棧幀時沒有內(nèi)存空間,JVM就會拋出StackOverflowError。
最常見的耗盡Java棧的案例是遞歸。在遞歸操作中,函數(shù)執(zhí)行時會調(diào)用自己。使用遞歸要小心,以免拋出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參數(shù)指定線程??臻g大小為1M,這個例子的執(zhí)行結(jié)果如下:
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
下面的例子演示了類之間有循環(huán)關(guān)系時的風險。
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());
}
}
當創(chuàng)建A對象的時候需要創(chuàng)建B對象,創(chuàng)建B對象的時候又要創(chuàng)建A對象,在兩個類之間形成了循環(huán)依賴,最終導致StackOverflowError。輸出結(jié)果:
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,找出行號的重復模式。這些重復的行號代表了被遞歸調(diào)用的代碼。仔細審查代碼,理解為何遞歸不終止。
- 如果你確認遞歸實現(xiàn)沒有問題,你可以通過-Xss參數(shù)增加棧的大小,這個參數(shù)可以在項目配置或命令行指定。