我正在调查Java应用程序正在产生的StackOverflowError。堆栈跟踪看起来像这样(对不起,我不能共享实际的生产跟踪):
at test.StackOverflowTest.foo(StackOverflowTest.java:24)
at test.StackOverflowTest.foo(StackOverflowTest.java:24)
at test.StackOverflowTest.foo(StackOverflowTest.java:24)
at test.StackOverflowTest.foo(StackOverflowTest.java:24)
...
从代码检查和单元测试来看,函数foo
本身似乎是正确的,这表明传递给foo
的数据存在问题。
问题在于,堆栈大小大于异常中堆栈跟踪的限制。这意味着未显示堆栈跟踪的开始,这使得进一步研究非常困难。
如何获取Java以显示堆栈跟踪的开始?
我相信可以将Java配置为减小堆栈大小或增加异常限制。但是,我担心在生产中调整这些值的其他影响。例如,如果Java使用堆栈中的前50个和后50个调用,那将更有帮助。
答案 0 :(得分:1)
如果您可以编辑生产代码并在某个地方运行它;您可以修改代码,以使stacktrace以特定的递归深度转储。这样您就可以“查看堆栈跟踪的底部”。
当然,您需要以兼容的方式修改生产代码(例如,不允许在foo
方法中添加“ depth”参数,因为这会影响您的客户)。
参见例如以下代码;我们将递归深度存储在线程局部变量中。
package lang;
/**
* run with -Dmy.debug.dump.enabled=true
*/
public class StackOverflowTest {
public static void main(String[] args) {
try {
StackOverflowTest o = new StackOverflowTest();
o.foo();
} catch (StackOverflowError err) {
System.out.println("err: StackOverflowError");
}
}
private static ThreadLocal<Integer> recurseCount = new ThreadLocal<Integer>() {
@Override
protected Integer initialValue() {
return 0;
};
};
private static final boolean DUMP_ENABLED;
static {
String sysprop = System.getProperty("my.debug.dump.enabled");
DUMP_ENABLED = sysprop!=null && "true".equals(sysprop);
}
// or set it via system properties
private static final int DUMP_ON_RECURSION_NUM=4;
class MyRecurseDump extends Exception {
public MyRecurseDump(String msg) {
super(msg);
}
private static final long serialVersionUID = 1L;
}
private void foo() {
try {
if (DUMP_ENABLED) {
recurseCount.set(recurseCount.get()+1);
if (recurseCount.get()==DUMP_ON_RECURSION_NUM) {
new MyRecurseDump("foo: reached num="+DUMP_ON_RECURSION_NUM+" recursion depth")
.printStackTrace(System.err);
}
}
// put foo code here
int x;
foo();
// end of foo code
//*********************************************
}
finally {
if (DUMP_ENABLED) {
recurseCount.set(recurseCount.get()-1);
}
}
}
}
使用java -Dmy.debug.dump.enabled=true lang.StackOverflowTest
运行它,输出为:
lang.StackOverflowTest$MyRecurseDump: foo: reached num=4 recursion depth
at lang.StackOverflowTest.foo(StackOverflowTest.java:44)
at lang.StackOverflowTest.foo(StackOverflowTest.java:53)
at lang.StackOverflowTest.foo(StackOverflowTest.java:53)
at lang.StackOverflowTest.foo(StackOverflowTest.java:53)
at lang.StackOverflowTest.main(StackOverflowTest.java:11)
err: StackOverflowError
可以通过多种方式进行调整(例如,更改递归深度);或仅对整个程序执行一次转储(因为您可能有多个转储)。
答案 1 :(得分:0)
您似乎在想,堆栈溢出错误就像本机程序中的缓冲区溢出异常一样,当存在写入尚未分配给该缓冲区的内存的风险,从而损坏了其他一些内存位置时。根本不是这样。
JVM为每个线程的每个堆栈分配了给定的内存,并且如果尝试调用方法的尝试恰巧填充了该内存,则JVM会引发错误。就像您试图在长度为N的数组的索引N处写入时一样。不会发生内存损坏。堆栈无法写入堆。
对于堆栈来说,StackOverflowError就是对堆的OutOfMemoryError:它只是表示没有可用的内存了。