Java if vs. try/catch overhead
Java是否有使用TIG/catch块的开销,而不是IF块(假设封闭代码不要求这样)?
例如,对字符串采用以下两种简单的"安全修剪"方法实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
如果
此外,使用
例如,在不影响代码其余部分的情况下,使用
在这种情况下,如果块(其中,
这样的模式也会加快方法的速度,特别是在错误条件很少出现的情况下,而且这样做不会影响程序的安全性(假设错误条件是"正常的",例如在字符串处理方法中,可以接受空值或空值,尽管很少出现)。
我知道你在问性能开销,但是你真的不应该交替使用
在我看来,您的示例代码强烈建议使用正确的方法,这里有一个
为了回答你的问题,我猜想一个
这个问题几乎是"死而复生",但我认为还有几点可以有用地提出:
对于非异常控制流使用EDCOX1 0是坏的风格(在Java中)。(经常有关于"非特殊"的含义的争论……但这是另一个话题。)
它不好的部分原因是
try / catch 比常规的控制流声明1昂贵很多。实际的区别取决于程序和平台,但我预计它的价格会高出1000倍或更多。除其他事情外,创建异常对象捕获一个堆栈跟踪,查找和复制关于堆栈上每个帧的信息。堆栈越深,需要复制的就越多。另一部分原因是它的样式不好,因为代码更难阅读。
1——Java 7的最新版本中的JIT可以在某些情况下优化异常处理以大幅降低开销。但是,默认情况下不会启用这些优化。
您编写示例的方式也存在问题:
抓到EDOCX1[2]是非常糟糕的做法,因为您有可能意外地抓到其他未经检查的异常。例如,如果你在给
raw.substring(1) 打电话时那样做,你也会抓住潜在的StringIndexOutOfBoundsException 的……隐藏虫子。您的示例所要做的是(可能)处理
null 字符串的不好实践的结果。作为一般原则,您应该尽量减少使用null 字符串,并尝试限制它们(有意)的传播。在可能的情况下,使用空字符串而不是null 表示"无值"。当您确实需要传递或返回一个null 字符串时,请在方法javadocs中清楚地记录它。如果您的方法在不应该调用时使用null 进行调用…这是一个错误。让它抛出一个异常。不要试图通过(在本例中)返回null 来补偿错误。
追随
My question was more general, not only for null values.
…我答案中的大多数点都不是关于空值的!
But please bear in mind that there are many cases where you want to allow the occasional null value, or any other value that could produce an exception, and just ignore them. This is the case, for example, when reading key/pair values from somewhere and passing them to a method like the tryTrim() above.
是的,在某些情况下,预期会有
但我认为,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | // Version 1 String param = httpRequest.getParameter("foo"); String trimmed = tryTrim(param); if (trimmed == null) { // deal with case of 'foo' parameter absent } else { // deal with case of 'foo' parameter present } // Version 2 String param = httpRequest.getParameter("foo"); if (param == null) { // deal with case of 'foo' parameter absent } else { String trimmed = param.trim(); // deal with case of 'foo' parameter present } // Version 3 String param = httpRequest.getParameter("foo"); if (param == null) { // treat missing and empty parameters the same param =""; } String trimmed = param.trim(); |
最后,您必须以不同于常规字符串的方式处理
我不是说
使用第二个版本。当其他替代方案可用时,不要使用控制流的异常,因为这不是它们的用途。例外情况适用于特殊情况。
在这个话题上,不要在这里抓住
否则,异常的抛出和捕获速度很快(虽然
就开销而言,您可以自己测试:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | public class Overhead { public static void main(String[] args) { String testString =""; long startTimeTry = System.nanoTime(); tryTrim(testString); long estimatedTimeTry = System.nanoTime() - startTimeTry; long startTimeIf = System.nanoTime(); ifTrim(testString); long estimatedTimeIf = System.nanoTime() - startTimeIf; System.out.println("Try time:" + estimatedTimeTry); System.out.println("If time:" + estimatedTimeIf); } public static String tryTrim(String raw) { try { return raw.trim(); } catch (Exception e) { } return null; } public static String ifTrim(String raw) { if (raw == null) { return null; } return raw.trim(); } |
}
我得到的数字是:
1 2 | Try time:8102 If time:1956 |
至于风格,这是一个完全独立的问题。if语句看起来很自然,但是尝试看起来很奇怪,原因有很多:-您捕获了异常,即使您正在检查空值,您是否希望发生"异常"情况(否则捕获NullException)?-你抓住了那个例外,你打算报告还是吞下?等。
编辑:看看我的评论,为什么这是一个无效的测试,但我真的不想让这个站在这里。只需交换trytrim和iftrim,我们就会突然得到以下结果(在我的机器上):
1 2 | Try time:2043 If time:6810 |
不是开始解释所有这些,只是从头读这个-克里夫也有一些关于整个主题的伟大幻灯片,但我现在找不到链接。
知道异常处理如何在热点中工作,我相当确定,在正确的测试中,无异常的Try/Catch将是基线性能(因为没有任何开销),但是JIT可以在空指针检查后进行方法调用(没有显式检查,但如果对象在这种情况下,我们会得到相同的结果。另外,不要忘记:我们讨论的是一个容易预测的差异,如果哪一个是一个CPU周期的话!修剪电话的费用是这个的一百万倍。