如何获得StackOverflowError的完整堆栈

Grz*_*zki 31 java stack-overflow

观察StackOverflowError时如何检索完整的调用堆栈?

考虑这个简单的例子:

public class Overflow {

    public Overflow() {
        new Overflow();
    }
    public static void a() {
        new Overflow();
    }
    public static void main(String[] argv) {
        a();
    }
}
Run Code Online (Sandbox Code Playgroud)

现在报告的错误是:

Exception in thread "main" java.lang.StackOverflowError
    at Overflow.<init>(Overflow.java:11)
    [last line repeated many times]
Run Code Online (Sandbox Code Playgroud)

但我无法在堆栈跟踪中看到maina方法.我的猜测是因为溢出,堆栈中的最新条目取代了最旧的条目(?).

现在,如何在输出中获取amain堆栈条目?

其背景是,我得到了一个的StackOverflowError(但是这不是一个无限递归,因为增加堆栈大小时,它不会发生),它是很难发现代码中的问题.我只获取了多行java.util.regex.Pattern代码而不是代码所谓的信息.应用程序太复杂,无法在每次调用Patterns时设置断点.

Cyr*_* Ka 40

JVM具有1024个条目的人为限制,您可以在异常或错误的堆栈跟踪中拥有这些条目,可能在发生时节省内存(因为VM必须分配内存来存储堆栈跟踪).

幸运的是,有一个标志允许增加此限制.只需使用以下参数运行程序:

-XX:MaxJavaStackTraceDepth=1000000
Run Code Online (Sandbox Code Playgroud)

这将打印多达100万个堆栈跟踪条目,这应该绰绰有余.也可以将此值0设置为将条目数设置为无限制.

此非标准JVM选项列表提供了更多详细信息:

最大.没有.Java异常的堆栈跟踪中的行(0表示全部).对于Java> 1.6,值0实际上意味着0.值-1或必须指定任何负数来打印所有堆栈(在Windows上使用1.6.0_22,1.7.0进行测试).如果Java <= 1.5,则值0表示所有内容,JVM在负数上阻塞(在Windows上使用1.5.0_22进行测试).

使用此标志运行问题的示例将得到以下结果:

Exception in thread "main" java.lang.StackOverflowError
    at Overflow.<init>(Overflow.java:3)
    at Overflow.<init>(Overflow.java:4)
    at Overflow.<init>(Overflow.java:4)
    at Overflow.<init>(Overflow.java:4)
(more than ten thousand lines later:)
    at Overflow.<init>(Overflow.java:4)
    at Overflow.<init>(Overflow.java:4)
    at Overflow.a(Overflow.java:7)
    at Overflow.main(Overflow.java:10)
Run Code Online (Sandbox Code Playgroud)

这样,即使实际堆栈跟踪长度超过1024行,您也可以找到引发错误的代码的原始调用者.

如果您不能使用该选项,还有另一种方法,如果您处于这样的递归函数中,并且您可以修改它.如果添加以下try-catch:

public Overflow() {
    try {
        new Overflow();
    }
    catch(StackOverflowError e) {
        StackTraceElement[] stackTrace = e.getStackTrace();
        // if the stack trace length is at  the limit , throw a new StackOverflowError, which will have one entry less in it.
        if (stackTrace.length == 1024) {
            throw new StackOverflowError();
        }
        throw e;  // if it is small enough, just rethrow it.
    }
}
Run Code Online (Sandbox Code Playgroud)

从本质上讲,这将创建并抛出一个新的StackOverflowError,丢弃最后一个条目,因为每个条目将比前一个条目向上发送一级(这可能需要几秒钟,因为必须创建所有这些错误).当堆栈跟踪将减少到1023个元素时,它只是被重新抛出.

最终,这将在堆栈跟踪的底部打印1023行,这不是完整的堆栈跟踪,但可能是它最有用的部分.

  • 注意:从 Java 9 开始,`-1` 不再是 MaxJavaStackTraceDepth 的正确值。 (3认同)