如何获得堆栈的StackOverflowError

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

考虑这个简单的例子:

public class Overflow {

    public Overflow() {
        new Overflow();
    }
    public static void a() {
        new Overflow();
    }
    public static void main(String[] argv) {
        a();
    }
}

现在报告的错误是:

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

但是我看不到堆栈跟踪中的maina方法。 我的猜测是这是因为溢出,栈上最新的条目取代了最老的(?)。

现在,如何获得输出中的amain栈条目?

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


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

幸运的是,有一个标志允许增加这个限制。 用下面的参数运行你的程序:

-XX:MaxJavaStackTraceDepth=1000000

这将打印多达100万条堆栈跟踪,这应该足够了。 也可以将此值设置为-1以将条目数设置为无限制。

非标准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)

这样,即使实际堆栈跟踪超过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.
    }
}

基本上,这将创建并抛出一个新的StackOverflowError ,放弃最后一个条目,因为每个条目都会比上一条条目发送一级(这可能需要几秒钟,因为必须创建所有这些错误)。 当堆栈跟踪将被减少到1023个元素时,它就会被重新抛出。

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


据我所知,这是不可能得到完整的堆栈跟踪(但是,我不知道为什么)。

但是,您可以采取的措施来跟踪问题,即手动检查受影响的代码中的堆栈深度,如下所示:

StackTraceElement[] trace = Thread.currentThread().getStackTrace();
if (trace.length > SOME_VALUE) {
  // trigger some diagnostic action, print a stack trace or have a breakpoint here
}

需要通过实验找到SOME_VALUE (足够高以便不会在“良好”情况下触发,并且足够低以至于无法触及)。 当然这会减慢你的代码,只能用于调试问题。

更新:我似乎错过了Pattern发生的问题,这使事情变得复杂。 但是,您可以在堆栈跟踪中的某个Pattern方法中使用条件方法断点,并使用如下条件(实际值可能需要调整):

Thread.currentThread().getStackTrace().length > 300

这样,当你点击断点时,你可以在堆栈的底部找到自己的代码。


如果您的堆栈用完了,请考虑创建一个专用线程,以提供足够的堆栈,特别是用于运行请求。 下面的示例代码。

package t1;

import java.util.concurrent.Callable;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
import java.util.regex.Pattern;

public class RegExpRunner {
    ExecutorService svc;    
    public RegExpRunner(long stackSize){
        init(stackSize);

    }


    void init(long stackSize){
        final SynchronousQueue<Runnable> queue = new SynchronousQueue<Runnable>();

        svc = new ThreadPoolExecutor(1, 2, 60, TimeUnit.SECONDS,  queue, createThreadFactory(stackSize), new RejectedExecutionHandler(){//wait if there is a concurrent compile and no available threads
            @Override
            public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
                try{
                    queue.put(r);
                }catch(InterruptedException _ie){
                    Thread.currentThread().interrupt();
                    throw new IllegalStateException(_ie);
                }
            }                   
        });
    }

    private ThreadFactory createThreadFactory(final long stackSize) {       
        return new ThreadFactory(){
            final ThreadGroup g = Thread.currentThread().getThreadGroup();
            private final AtomicLong counter= new AtomicLong();
            {
                //take care of contextClassLoader and AccessControlContext              
            }

            @Override
            public Thread newThread(Runnable r) {               
                Thread t = new Thread(g, r, composeName(r), stackSize);
                return t;
            }

            protected String composeName(Runnable r) {
                return String.format("Regexp dedicated compiler: %d @ %tF %<tT ", counter.incrementAndGet(), System.currentTimeMillis());
            }   
        };
    };

    public Pattern compile(final String regex){//add flags if you need 'em
        Callable<Pattern> c = new Callable<Pattern>(){
            @Override
            public Pattern call() throws Exception {
                return Pattern.compile(regex);
            }           
        };

        try{
            Pattern p = svc.submit(c).get();
            return p;
        }catch(InterruptedException _ie){
            Thread.currentThread().interrupt();
            throw new IllegalStateException(_ie);
        } catch(CancellationException _cancel){
            throw new AssertionError(_cancel);//shan't happen
        } catch(ExecutionException _exec){
            Throwable t = _exec.getCause();
            if (t instanceof RuntimeException) throw (RuntimeException) t;
            if (t instanceof Error) throw (Error) t;
            throw new IllegalStateException(t==null?_exec:t);
        }


    }
}
链接地址: http://www.djcxy.com/p/80453.html

上一篇: How to get full stack of StackOverflowError

下一篇: What is a StackOverflowError?