【问题标题】:Overhead associated with Exception vs Throwable in Java与 Java 中的 Exception 与 Throwable 相关的开销
【发布时间】:2011-01-08 22:26:48
【问题描述】:

我知道

throw new Exception();

具有相当大的开销,因为它会创建完整的 stackTrace 等。
有没有

throw new Throwable();

出现同样的问题?这种行为是继承的,还是抛出 Throwable 的开销更小(没有)?

编辑
分析员的角度来看,用户输入错误的密码是程序正常执行顺序的一个例外。所以如果我有:

public Session newSession() {  
  validate_user_and_password();   
}

分析师的角度来看,抛出 UserNotValidException 听起来是正确的。
如果你的代码有很好的抽象,返回 null0 听起来是不正确的。我只是想知道我是否可以在代码中真正实现这一点,或者我是否必须把它留给理论。

编程观点异常和分析师观点异常之间有很大的区别。

注意:我给出了一个非常简单和愚蠢的例子,这不是我的情况。
注意 2:我知道返回 null 是很平常的事情,但我需要有适当的抽象和 OO 代码,而且我个人认为这没有什么坏处。

【问题讨论】:

  • 为什么会这样?功能要求是什么?
  • 无论如何你都不应该在常规情况下使用异常——它们应该只在异常的情况下使用。
  • @anon 它们应该用于“异常代码路径”,它可能会或可能不会像人们想象的那样异常。例如,如果您避免填充堆栈跟踪,最好使用它们来发出流结束的信号,而不是在每次读取后检查结束。 (但这不适用于遍历数组,其中 VM 知道全长。)
  • 认为他的意思是“业务分析师”中的“分析师”。
  • “annalist”的意思是“历史学家”。

标签: java exception throwable


【解决方案1】:

Throwable 在创建时也会创建一个堆栈跟踪。来自java docs for Throwable

throwable 包含其线程在创建时的执行堆栈快照。

因此,就创建堆栈跟踪的开销而言,ExceptionThrowable 之间应该没有区别。

如果您对“异常事件”使用异常(应该如此),那么您不必太在意堆栈跟踪的开销。运行代码中很少发生异常事件。所以异常不应该以任何显着的方式影响正常代码的性能。

【讨论】:

    【解决方案2】:

    不,您需要自己的子类来避免这种影响。

    Exception ex = new Exception() {
        @Override public Throwable fillInStackTrace() {
            return this; // and do nothing else
        }
    };
    

    这会创建一个不会填充堆栈跟踪的异常实例(异常的创建委托给fillInStackTrace 以实际填充堆栈跟踪),因此创建起来很便宜。

    【讨论】:

    • 看来return null; 会和return this; 做同样的事情。有什么理由要使用return this;
    • 遵守 Throwable 的 API 文档。
    • 但是您破坏了“此方法在此 Throwable 对象中记录有关当前线程的堆栈帧的当前状态的信息。”编辑:但是点,巧妙的把戏!
    • 嘿嘿嘿,好点子……戴上我的律师帽:“但它并没有说记录了完整信息,所以零信息满足了这个要求”这是什从工程的角度来看一个很好的论据:外部客户端可能依赖于不获取空对象但不填充堆栈跟踪,这只会在调用getStackTrace 时导致一个空数组,因此它不太可能破坏代码。 (嗯,我实际上应该验证最后一点,我什至不确定,你也可能得到 null。)
    • Scala 在 ControlFlowException 中使用这种技术来实现break
    【解决方案3】:

    使用 JIT 编译,实际上在 Java 中抛出 Exception 的情况并非如此。但是抛出 Throwable 并没有太大的不同,因为你也会在那里得到一个堆栈跟踪。

    如果您有兴趣,有一篇非常有趣的论文,名为“即时编译中的高效 Java 异常处理”(link)。读起来不轻松,但信息量很大。

    【讨论】:

    • 你能提供一个参考吗?我认为,当您获得 JIT 时,创建堆栈跟踪的成本甚至更高,因为您必须取消优化才能恢复实际的堆栈跟踪。
    • IIRC,我从创建异常开始测量了超过 10k 个周期。那是几年前的事了。使用完整的“框架”堆栈跟踪可能会更多。
    • 论文是2000年的,现在不是过时了吗?我只是要求确保在阅读之前 :)
    【解决方案4】:

    java.lang.Exception 扩展了java.lang.Throwable,因此开销相同。来自Javadoc

    Throwable 类是 Java 语言中所有错误和异常的超类。只有作为此类(或其子类之一)的实例的对象才会被 Java 虚拟机抛出或者可以被 Java throw 语句抛出。同样,只有此类或其子类之一可以是 catch 子句中的参数类型。

    两个子类的实例,Error 和 Exception,通常用于指示发生了异常情况。通常,这些实例是在异常情况的上下文中新创建的,以便包含相关信息(例如堆栈跟踪数据)。

    【讨论】:

    • 这种逻辑毫无意义——子类可能会引入更多的复杂性。当然在这种情况下它不会 - 但这一事实并没有让你的答案变得毫无用处。
    【解决方案5】:

    你不应该抛出或捕获Throwable. 异常的范围太大了。

    如前所述,应仅在需要时使用异常,即:在特殊情况下,并且应针对产生它们的情况。除此之外,捕获Throwable 意味着许多异常,例如OutOfMemoryException。这种量级的错误无法(轻松)恢复,并且不应由开发人员处理。

    【讨论】:

    • 我同意,永远不应该throw new Throwable(),但是[在问我最初的问题时],我正在考虑throw new MyNonStackTracedException()MyNonStackTracedException extends Throwable(如果没有开销)。跨度>
    • 你的建议不是很好。告诉程序员他们不应该处理异常有点愚蠢。您可能至少想在程序崩溃之前告诉您的用户“嘿,我们内存不足: (”。
    • 你误解了。 OP 询问有关投掷 ThrowableException 的问题。如果你抛出Throwable,你需要抓住Throwable,它有很多很多相关的问题。
    【解决方案6】:

    Throwable 是异常的父类。所以Exception class继承自Throwable

    【讨论】:

      【解决方案7】:

      Java Exception

      正如@mangoDrunk 所说:“Throwable 是异常和错误的超类。”

      【讨论】:

      • 超类,从我在图片上看到的内容。
      【解决方案8】:

      您可以查看这两个类的源代码,发现Exception 除了公开与Throwable 相同的构造函数之外没有做任何事情。所有的肉,因此开销,都存在于Throwable

      即使Exception 确实引入了一些额外的开销,使用Throwable 来代替也是明显的过度优化。为工作使用正确的工具,不要仅仅因为它更轻而选择错误的工具。

      【讨论】:

        猜你喜欢
        • 2011-10-04
        • 2010-12-04
        • 1970-01-01
        • 2014-10-13
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多