【问题标题】:Exception without stack trace in JavaJava中没有堆栈跟踪的异常
【发布时间】:2012-07-11 04:13:26
【问题描述】:

这可能是一个非常幼稚的问题。

我曾经相信 Java 中的 Throwable总是包含堆栈跟踪。对吗?

现在看起来我在没有堆栈跟踪的情况下捕获了异常。是否有意义? 可能在没有堆栈跟踪的情况下捕获异常吗?

【问题讨论】:

  • 什么JVM?环境?等等。这有帮助吗? stackoverflow.com/questions/4659151/…
  • @SB。是的,这确实有帮助。非常感谢。我有一个非常相似的问题:我有很多例外(NPE)。 log4j 的方法 error 记录一些异常没有堆栈跟踪。

标签: java exception stack-trace


【解决方案1】:

可以在没有堆栈跟踪的情况下在 Java 中捕获 Throwable 对象:

Throwable(String message, Throwable cause, boolean enableSuppression,boolean writableStackTrace) 

用 指定的详细信息、原因、启用或禁用抑制,以及 启用或禁用可写堆栈跟踪

public Throwable fillInStackTrace()

填写执行堆栈跟踪。这个方法记录在这个 关于堆栈当前状态的可抛出对象信息 当前线程的帧。

如果此 Throwable 的堆栈跟踪不可写,则调用此 方法无效

http://docs.oracle.com/javase/7/docs/api/java/lang/Throwable.html

【讨论】:

  • 它只是说启用或禁用“可写堆栈跟踪”.. 我不认为这意味着它不会捕获堆栈跟踪
  • 是不是就不会生成了,过程会快很多?
  • writableStackTrace 的文档说:“堆栈跟踪是否应该可写”。因此,该参数不会阻止生成堆栈跟踪。我不明白这篇文章如何回答这个问题。请修复或删除此答案!
  • 请阅读问题。 OP 询问是否可以在没有堆栈跟踪的情况下捕获 Throwable。答案当然是肯定的。直接引用fillInStackTrace 的文档:“填充执行堆栈跟踪。此方法在此 Throwable 对象中记录有关当前线程的堆栈帧的当前状态的信息。如果此 Throwable 的堆栈跟踪不可写,则调用此方法无效。”
【解决方案2】:

对于 Java 6:

由于 Java 6 没有 Throwable(String message, Throwable cause, boolean enableSuppression,boolean writableStackTrace) 构造函数,我们可以使用以下技术抑制堆栈跟踪填充(借用自 Scala,从 How slow are Java exceptions? 得知)

class NoStackTraceRuntimeException extends RuntimeException {
    @Override
    public synchronized Throwable fillInStackTrace() {
        return this;
    }
}

用法相同:throw new NoStackTraceRuntimeException (),或者它的子类型。

我们也可以通过扩展Throwable来做同样的事情:

class NoStackTraceThrowable extends Throwable {
    @Override
    public synchronized Throwable fillInStackTrace() {
        return this;
    }
}

但是,一个小问题是您不能再使用Exceptioncatch 这些异常,因为这不是Exception 的子类型,而是应该捕获NoStackTraceThrowable 或其子类型。

更新:有关不同用例中性能的一些有趣统计数据,请查看SO question

【讨论】:

  • @shekharsuman 不,但不是 Java 7。这个答案可能对使用 Java 6 的人有所帮助 :)
  • 即使在 Java 1.6 之外,这仍然是一种可行的方法。这将抑制整个堆栈跟踪(因为将打印出传入的 4 参数构造函数的“原因”)。
  • 我在 2016 年仍在使用 Java 6,所以是的:/
  • 如果您希望catch (Exception) 块捕获您的NoStackTraceThrowable,则可以改为扩展Exception(并称为NoStackTraceException)。
  • 没有必要将仅仅返回this的方法标记为synchronized
【解决方案3】:

对于 Java 7+,这里是一个异常示例,可以选择抑制堆栈跟踪。

public class SuppressableStacktraceException extends Exception {

    private boolean suppressStacktrace = false;

    public SuppressableStacktraceException(String message, boolean suppressStacktrace) {
        super(message, null, suppressStacktrace, !suppressStacktrace);
        this.suppressStacktrace = suppressStacktrace;
    }

    @Override
    public String toString() {
        if (suppressStacktrace) {
            return getLocalizedMessage();
        } else {
            return super.toString();
        }
    }
}

这可以通过以下方式证明:

try {
    throw new SuppressableStacktraceException("Not suppressed", false);
} catch (SuppressableStacktraceException e) {
    e.printStackTrace();
}
try {
    throw new SuppressableStacktraceException("Suppressed", true);
} catch (SuppressableStacktraceException e) {
    e.printStackTrace();
}

这是基于来自Apache SystemML 的 MLContextException,其代码可在 GitHub 上的https://github.com/apache/systemml 获得。

【讨论】:

  • 整洁,删除 toString() 仍然为我提供了足够的抑制。
  • 你为什么不暴露一个需要投掷的构造函数参数?
  • 为什么 supressStackTrace 在 Exception 中隐含 supressedException 参数?
  • 为什么需要覆盖toString
【解决方案4】:

抑制任何异常的堆栈跟踪的最简单方法是

throwable.setStackTrace(new StackTraceElement[0]);

如果异常有原因,你可能需要递归地做同样的事情。

这也尽可能降低了创建堆栈跟踪的成本

throwable 的堆栈跟踪在

中初始化
Throwable#fillInStackTrace()

,由任何构造函数调用,因此无法避免。 当实际使用stacktrace时,会在

中懒惰地构造一个StackTraceElement[]
Throwable#getOurStackTrace()

只有在 Throwable.stackTrace 字段尚未设置时才会发生。

将堆栈跟踪设置为任何非空值,避免在 Throwable#getOurStackTrace() 中构造 StackTraceElement[] 并尽可能降低性能损失。

【讨论】:

  • 这行得通,但它并没有解决大多数人想通过抑制堆栈跟踪来解决的问题。生成堆栈跟踪很慢,这会减慢大量使用异常进行消息传递的代码。为了解决这个问题,必须防止堆栈跟踪的创建,而不是在它们已经创建时删除它们。 - 也就是说,当由于某种原因存储了许多无法控制的异常时,您的方法对于节省内存仍然有用。
  • Halo @Hans Adler,我理解您的担忧,但我认为我的回答也尽可能减少了创建堆栈跟踪的代价高昂。请参阅我的上次编辑。
  • “[Throwable#fillInStackTrace()] 被任何构造函数调用,因此无法避免。” – 您可以重写此方法以使其成为空操作.
【解决方案5】:

正如 NG. 的回复中所暗示的,如果您一开始看到堆栈跟踪,但随后由于相同的异常而消失,您很可能看到了 JVM 优化的效果。 在这种情况下,以下问题非常相似。

Recurring Exception without a stack trace - how to reset?

NullPointerException in Java with no StackTrace

您可以关闭此 JVM 功能,但我建议保持启用它并努力从一开始就防止错误,或者捕获它并更优雅地处理它。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-12-03
    • 1970-01-01
    • 2011-01-05
    • 2011-10-21
    • 2012-09-04
    • 2017-08-06
    • 2010-09-13
    相关资源
    最近更新 更多