java.lang.StackOverflowError——如何解决StackOverflowError错误

原文链接:https://examples.javacodegeeks.com/java-basics/exceptions/java-lang-stackoverflowerror-how-to-solve-stackoverflowerror/

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参数增加栈的大小,这个参数可以在项目配置或命令行指定。
    原文作者:quiterr
    原文地址: https://www.jianshu.com/p/e4e224b87aa3
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞