这篇文章是关于历史经验以及最近应用的性能优化技术的。 几年前,我在特定的应用程序中发誓,我不得不发现隐藏在真正聪明的工程“技术”之下的无证行为。
它是一个典型的用于发票的单片Java EE应用程序。 最好忘记确切的代码,但是我记得开发人员已经找到了一种真正聪明的方法来控制业务流程。
流程的一部分通常是无休止的if-then-else混乱,但使事情更加“令人兴奋”的是,这些检查的一些随机元素被埋入了自定义java.lang.RuntimeException处理机制中。 因此,您可能具有类似于以下内容的代码:
if (invoice.overdue()) {if (invoice.getCustomer().isKeyCustomer())throw new InvoiceOverdueException(InvoiceOverdueException.KEY_CUSTOMER);elsedoSomething();
}
else {if (invoice.getDueAmount() > BIG_AMOUNT)if (invoice.getCustomer().isKeyCustomer())//be silentelsethrow new InvoiceExceededException(invoice.getDueAmount());
}
并非像上述那样简短易懂的块,而是在整个应用程序中散布了数千行代码。
我想你可能同意我的观点,这是使自己不可或缺的一种好方法。 有人会以一种疯狂的方式来理解应用程序为什么会如此。
我回想起最近的Plumbr优化任务带来的经验。 我想说我们的代码没有使用前面案例描述的异常,但是不幸的是,这并非完全正确。 一个特定的方法仍然在代码的常规流程中构造并抛出RuntimeException 。 由于这个特定模块的性能异常,我只发现了这个孤独的反派。
通常,仅在遇到意外问题时才会引发异常。 因此,我们不希望每个线程每秒抛出数千个异常。 但是像我一样,您可能会发现一种对异常事件使用异常的方法。
我之所以只找到罪魁祸首,是因为这是特定图形遍历算法中经常使用的代码块,因此从中挤出最后一毫秒至关重要。 立即删除异常处理使此代码块的完成速度提高了100倍以上。
这可能使您想知道–为什么异常处理速度很慢? 最慢的部分与构造异常有关。 或者,更确切地说,是java.lang.Throwable的任何子类。
如果您还记得的话,所有构造函数都会通过调用super()来调用对超类默认构造函数的调用。 如果您未自行指定此调用,则编译器会友好地将其添加到字节码本身中。 无论如何,当查看java.lang.Throwable源代码时,您会看到答案盯着您:
public Throwable() {fillInStackTrace();}public synchronized Throwable fillInStackTrace() {if (stackTrace != null ||backtrace != null /* Out of protocol state */ ) {fillInStackTrace(0);stackTrace = UNASSIGNED_STACK;}return this;}private native Throwable fillInStackTrace(int dummy);
因此,每次创建新的Throwable()时,最终都会通过本机调用填充整个堆栈跟踪。 如果您不认为这很慢,请执行以下jmh微基准测试,以验证创建异常的费用比构造常规对象的费用高数百倍:
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
public class NewExceptionTest {@GenerateMicroBenchmarkpublic Object baseline() {return new Object();}@GenerateMicroBenchmarkpublic Object exceptional() {return new RuntimeException();}
}
Benchmark Mode Thr Cnt Sec Mean Mean error Units
j.NewExceptionTest.baseline avgt 1 5 5 3.275 0.029 nsec/op
j.NewExceptionTest.exceptional avgt 1 5 5 1329.266 8.675 nsec/op
总而言之,在特殊情况下,名称应表示例外。 如果您开始滥用该概念,那么您要么使代码不可读,要么开始遭受性能问题的困扰。 至少,我保证您会从中获得大量的负面因果报应。
翻译自: https://www.javacodegeeks.com/2013/08/throwing-exceptions-slow-and-ugly.html