如何查看StackOverflowError的堆栈跟踪的开头[重复]

问题描述 投票:-4回答:2

我正在调查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个调用,那将会更有帮助。

java exception stack-overflow
2个回答
1
投票

如果您可以编辑生产代码并在某处运行它;您可以修改代码,以便在特定的递归深度转储堆栈跟踪。这允许你“看到堆栈跟踪的底部”。

当然,您需要以兼容的方式修改生产代码(例如,不允许在foo方法中添加“深度”参数 - 因为这会影响您的客户)。

见例如。以下代码;我们将递归深度存储在线程局部变量中。

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

它可以通过多种方式进行调整(在示例中更改递归深度);或者只在整个程序中执行单个转储(因为您可能有多个转储)。


0
投票

您可能认为堆栈溢出错误就像本机程序中的缓冲区溢出异常,当存在写入尚未为缓冲区分配的内存的风险时,从而破坏其他一些内存位置。事实并非如此。

JVM具有为每个线程的每个堆栈分配的给定内存,并且如果尝试调用方法恰好填充此内存,则JVM会抛出错误。就像你试图在长度为N的数组的索引N处写一样。没有内存损坏可能发生。堆栈无法写入堆。

StackOverflowError是堆栈OutOfMemoryError对堆的唯一信号:它只是表示没有更多可用内存。

© www.soinside.com 2019 - 2024. All rights reserved.