是否指定例外?

时间:2013-06-17 16:11:06

标签: java performance exception try-catch

我有一个性能问题,在使用try catch子句时,最好指定一下你可以得到的确切异常,或者只是使用异常它会更好吗? 例如:

try {
    whatever
} catch (NullPointerException ex) {
    whatever
}

或者如果你不介意什么样的例外:

try {
    whatever
} catch (Exception ex) {
    whatever
}

因为我知道你可以使用不同的例外来触发不同的效果,但我只是要求表现。

5 个答案:

答案 0 :(得分:4)

根据我的测试,在性能上没有显着差异

每次运行都会尝试每个方案中的一千万个,然后比较运行时间(纳秒)和舍入秒数。这实际上与我原来的假设相反,因为我认为捕捉Throwable会显示出明显的改善。

我也开始意识到部分原因可能是由于优化器的影响,所以我创建了一个更复杂的例子,其中包括下面的伪随机数,认为这将减轻优化器对其产生的任何潜在影响。代码。

(我不会就正确使用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)");

    }

}

更多复杂的伪随机码

我创建了这个,以确保优化器不是简单地“忽略”整个throw / catch进程,因为它意识到代码块将始终流向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)");

    }

}

答案 1 :(得分:2)

我建议这里正确的答案是根据程序原因而不是性能使用适当的异常处理。如果(忽略性能)捕获NullPointerException更合适,那么就这样做。

例外应该是例外情况。它们应该很少发生,因为异常处理中的性能应该不如正确性重要。

如果您的应用程序经常预测到这种情况,那么它应该通过除Exception之外的某种机制来处理它。如果您担心性能,尤其如此,因为抛出异常总是很昂贵。

答案 2 :(得分:1)

在这种情况下,都没有。 try / catch比较昂贵,应该谨慎使用。你最好手动检查null而不是捕获NullPointerException。

答案 3 :(得分:0)

如果在异常捕获方面表现与任何方式相关,则不例外。

所有它正在做的是测试,所以我怀疑它是否有任何真正的区别。

无论如何,捕捉异常应该非常罕见。

如果你在做

try 
{
    whatever
} 
catch (MyException10 ex10) 
{
    whatever
}
catch (MyException9 ex9) 
{
    whatever
}
...
catch (MyException1 ex1) 
{
    whatever
}

然后你可以争论一个潜在的性能改进,但这会改变你的代码,所以有一点没有抛出无数例外......

答案 4 :(得分:0)

我可以提出的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(包括ExceptionNullPointerException和其他细节)时,会抓住所有ArrayIndexOutOfBoundsException个{{1}}。这不好(tm)。