我有一点性能问题,在使用 try catch 子句时,最好指定您可以获得的确切异常,还是只使用异常更好?例子:
try {
whatever
} catch (NullPointerException ex) {
whatever
}
或者如果你不介意什么样的例外:
try {
whatever
} catch (Exception ex) {
whatever
}
因为我知道您可以使用不同的异常来触发不同的效果,但我只是要求性能。
我有一点性能问题,在使用 try catch 子句时,最好指定您可以获得的确切异常,还是只使用异常更好?例子:
try {
whatever
} catch (NullPointerException ex) {
whatever
}
或者如果你不介意什么样的例外:
try {
whatever
} catch (Exception ex) {
whatever
}
因为我知道您可以使用不同的异常来触发不同的效果,但我只是要求性能。
根据我的测试,性能没有显着差异。
每次运行尝试每个场景一千万次,然后比较运行时间(以纳秒为单位)以及四舍五入的秒数。这实际上与我最初的假设相反,因为我认为抓住 aThrowable
会显示出明显的改善。
我也开始意识到这部分可能是由于优化器的影响,因此我创建了一个更复杂的示例,其中包括下面的伪随机数,认为这将减轻优化器对代码的任何潜在影响。
(我不会教你如何正确使用catch
块,因为问题是关于性能的,而不是最佳实践。)
大量数据低于这一点!
运行 1 结果:
Exception: 7196141955 (7.196s)
NumberFormatException: 7736401837 (7.736s)
Throwable: 6818656505 (6.819s)
运行 2 结果:
Exception: 7262897545 (7.263s)
NumberFormatException: 7056116050 (7.056s)
Throwable: 7108232206 (7.108s)
运行 3 次结果:
Exception: 7088967045 (7.089s)
NumberFormatException: 7020495455 (7.020s)
Throwable: 7192925684 (7.193s)
运行 4 个结果:
Exception: 6916917328 (6.917s)
NumberFormatException: 7690084994 (7.690s)
Throwable: 6906011513 (6.906s)
运行 5 个结果:
Exception: 7247571874 (7.248s)
NumberFormatException: 6818511040 (6.819s)
Throwable: 6813286603 (6.813s)
代码
import java.math.BigDecimal;
import java.math.RoundingMode;
public class Test {
private static final int TRIALS = 10000000;
private static final int NANOS_IN_SECOND = 1000000000;
private static final int DECIMAL_PRECISION = 3;
private static final RoundingMode ROUNDING_MODE = RoundingMode.HALF_UP;
public static void main(String[] args) {
long firstStart = System.nanoTime();
for(int i = 0; i < TRIALS; i++) {
try {
throw new NumberFormatException();
}
catch(Exception e) {
}
}
long firstEnd = System.nanoTime();
long secondStart = System.nanoTime();
for(int i = 0; i < TRIALS; i++) {
try {
throw new NumberFormatException();
}
catch(NumberFormatException e) {
}
}
long secondEnd = System.nanoTime();
long thirdStart = System.nanoTime();
for(int i = 0; i < TRIALS; i++) {
try {
throw new NumberFormatException();
}
catch(Throwable e) {
}
}
long thirdEnd = System.nanoTime();
long exception = firstEnd - firstStart;
long numberFormatException = secondEnd - secondStart;
long throwable = thirdEnd - thirdStart;
BigDecimal exceptionSeconds = new BigDecimal((double)exception / (double)NANOS_IN_SECOND);
BigDecimal numberFormatExceptionSeconds = new BigDecimal((double)numberFormatException / (double)NANOS_IN_SECOND);
BigDecimal throwableSeconds = new BigDecimal((double)throwable / (double)NANOS_IN_SECOND);
exceptionSeconds = exceptionSeconds.setScale(DECIMAL_PRECISION, ROUNDING_MODE);
numberFormatExceptionSeconds = numberFormatExceptionSeconds.setScale(DECIMAL_PRECISION, ROUNDING_MODE);
throwableSeconds = throwableSeconds.setScale(DECIMAL_PRECISION, ROUNDING_MODE);
System.out.println("Exception: " + exception + " (" + exceptionSeconds + "s)");
System.out.println("NumberFormatException: " + numberFormatException + " (" + numberFormatExceptionSeconds + "s)");
System.out.println("Throwable: " + throwable + " (" + throwableSeconds + "s)");
}
}
更复杂的伪随机代码
我创建它是为了确保优化器不会通过意识到代码块将始终流向catch
. 通过尝试Integer.parseInt()
随机选择的String
(但总是无效的),这意味着编译器直到运行时才能知道通过for()
循环的给定运行是否有效。
正如第一个实验所预期的那样,三种情况之间没有显着差异。
运行 1 结果:
Exception: 10988431371 (10.988s)
NumberFormatException: 11360698958 (11.361s)
Throwable: 10539041505 (10.539s)
运行 2 结果:
Exception: 12468860076 (12.469s)
NumberFormatException: 11852429194 (11.852s)
Throwable: 11859547560 (11.860s)
运行 3 次结果:
Exception: 10618082779 (10.618s)
NumberFormatException: 10718252324 (10.718s)
Throwable: 10327709072 (10.328s)
运行 4 个结果:
Exception: 11031135405 (11.031s)
NumberFormatException: 10689877480 (10.690s)
Throwable: 10668345685 (10.668s)
运行 5 个结果:
Exception: 11513727192 (11.514s)
NumberFormatException: 11581826079 (11.582s)
Throwable: 12488301109 (12.488s)
代码
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Random;
public class Test {
private static final int TRIALS = 10000000;
private static final int NANOS_IN_SECOND = 1000000000;
private static final int DECIMAL_PRECISION = 3;
private static final RoundingMode ROUNDING_MODE = RoundingMode.HALF_UP;
private static final String[] TEST_STRINGS = {
"lawl",
"rofl",
"trololo",
"foo",
"bar"
};
private static final Random RANDOM = new Random();
public static void main(String[] args) {
long firstStart = System.nanoTime();
for(int i = 0; i < TRIALS; i++) {
try {
Integer.parseInt(TEST_STRINGS[RANDOM.nextInt(TEST_STRINGS.length)]);
}
catch(Exception e) {
}
}
long firstEnd = System.nanoTime();
long secondStart = System.nanoTime();
for(int i = 0; i < TRIALS; i++) {
try {
Integer.parseInt(TEST_STRINGS[RANDOM.nextInt(TEST_STRINGS.length)]);
}
catch(NumberFormatException e) {
}
}
long secondEnd = System.nanoTime();
long thirdStart = System.nanoTime();
for(int i = 0; i < TRIALS; i++) {
try {
Integer.parseInt(TEST_STRINGS[RANDOM.nextInt(TEST_STRINGS.length)]);
}
catch(Throwable e) {
}
}
long thirdEnd = System.nanoTime();
long exception = firstEnd - firstStart;
long numberFormatException = secondEnd - secondStart;
long throwable = thirdEnd - thirdStart;
BigDecimal exceptionSeconds = new BigDecimal((double)exception / (double)NANOS_IN_SECOND);
BigDecimal numberFormatExceptionSeconds = new BigDecimal((double)numberFormatException / (double)NANOS_IN_SECOND);
BigDecimal throwableSeconds = new BigDecimal((double)throwable / (double)NANOS_IN_SECOND);
exceptionSeconds = exceptionSeconds.setScale(DECIMAL_PRECISION, ROUNDING_MODE);
numberFormatExceptionSeconds = numberFormatExceptionSeconds.setScale(DECIMAL_PRECISION, ROUNDING_MODE);
throwableSeconds = throwableSeconds.setScale(DECIMAL_PRECISION, ROUNDING_MODE);
System.out.println("Exception: " + exception + " (" + exceptionSeconds + "s)");
System.out.println("NumberFormatException: " + numberFormatException + " (" + numberFormatExceptionSeconds + "s)");
System.out.println("Throwable: " + throwable + " (" + throwableSeconds + "s)");
}
}
我建议这里的正确答案是根据程序原因使用适当的异常处理,而不是性能。如果(忽略性能)捕获 a then 会更合适NullPointerException
。
例外应该是例外情况。它们应该很少发生,因此异常处理中的性能应该不如正确性重要。
如果您的应用程序定期预测这种情况,那么它应该通过异常以外的某种机制来处理它。如果您担心性能,尤其如此,因为抛出异常总是很昂贵。
在这种情况下,两者都不是。try/catch 比较昂贵,应该谨慎使用。手动检查 null 比捕获 NullPointerException 更好。
如果性能在任何方面与异常捕获相关,那么它不是异常。
它所做的只是一个测试,所以我怀疑它有什么真正的区别。
无论如何,捕获异常应该非常罕见。
如果你在做
try
{
whatever
}
catch (MyException10 ex10)
{
whatever
}
catch (MyException9 ex9)
{
whatever
}
...
catch (MyException1 ex1)
{
whatever
}
然后你可以争辩潜在的性能改进,但这将是重新编写你的代码,所以一点不会引发无数异常......
我能想到的 try/catch 块的最佳性能建议是:让它们变小。
如果您有一段代码可以抛出三个异常,请执行以下操作:
try {
piece1();
} catch (Exception1 e) {
// whatever
}
try {
piece2();
} catch (Exception2 e) {
// whatever
}
try {
piece3();
} catch (Exception3 e) {
// whatever
}
这样,你可以//return
早点。并以应有的方式对待每个异常。您很少“不介意”捕获到什么异常。break
continue
另请注意,过于宽泛的catch
块可能会“隐藏”您不希望抛出的异常。特别是,RuntimeException
当您捕获时,所有 s 都会被捕获Exception
(包括NullPointerException
,ArrayIndexOutOfBoundsException
和其他细节)。这不好(tm)。