如何获得堆栈的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]
但是我看不到堆栈跟踪中的main
和a
方法。 我的猜测是这是因为溢出,栈上最新的条目取代了最老的(?)。
现在,如何获得输出中的a
和main
栈条目?
背景是我得到了一个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