首页 > 技术文章 > 啪啪,打脸了!领导说:try-catch必须放在循环体外!

otis 2020-06-03 09:36 原文

本文摘自掘金,原文:https://juejin.im/post/5ed5b998f265da76bd1ad012
只为记录,方便查阅。

今天给大家带来的是关于 try-catch 应该放在循环体外,还是放在循环体内的文章,我们将从性能和业务场景分析这两个方面来回答此问题。

很多人对 try-catch 有一定的误解,比如我们经常会把它(try-catch)和“低性能”直接画上等号,但对 try-catch 的本质(是什么)却缺少着最基础的了解,因此我们也会在本篇中对 try-catch 的本质进行相关的探索。

性能评测
话不多说,我们直接来开始今天的测试,本文我们依旧使用 Oracle 官方提供的 JMH(Java Microbenchmark Harness,JAVA 微基准测试套件)来进行测试。

首先在 pom.xml 文件中添加 JMH 框架,配置如下:

org.openjdk.jmh jmh-core {version} 复制代码 完整测试代码如下:

import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;

import java.util.concurrent.TimeUnit;

/**

  • try - catch 性能测试
    */
    @BenchmarkMode(Mode.AverageTime) // 测试完成时间
    @OutputTimeUnit(TimeUnit.NANOSECONDS)
    @Warmup(iterations = 1, time = 1, timeUnit = TimeUnit.SECONDS) // 预热 1 轮,每次 1s
    @Measurement(iterations = 5, time = 5, timeUnit = TimeUnit.SECONDS) // 测试 5 轮,每次 3s
    @Fork(1) // fork 1 个线程
    @State(Scope.Benchmark)
    @Threads(100)
    public class TryCatchPerformanceTest {
    private static final int forSize = 1000; // 循环次数
    public static void main(String[] args) throws RunnerException {
    // 启动基准测试
    Options opt = new OptionsBuilder()
    .include(TryCatchPerformanceTest.class.getSimpleName()) // 要导入的测试类
    .build();
    new Runner(opt).run(); // 执行测试
    }

    @Benchmark
    public int innerForeach() {
    int count = 0;
    for (int i = 0; i < forSize; i++) {
    try {
    if (i == forSize) {
    throw new Exception("new Exception");
    }
    count++;
    } catch (Exception e) {
    e.printStackTrace();
    }
    }
    return count;
    }

    @Benchmark
    public int outerForeach() {
    int count = 0;
    try {
    for (int i = 0; i < forSize; i++) {
    if (i == forSize) {
    throw new Exception("new Exception");
    }
    count++;
    }
    } catch (Exception e) {
    e.printStackTrace();
    }
    return count;
    }
    }
    复制代码
    以上代码的测试结果为:


以上结果可以看出,程序在循环 1000 次的情况下,单次平均执行时间为:

循环内包含 try-catch 的平均执行时间是 635 纳秒 ±75 纳秒,也就是 635 纳秒上下误差是 75 纳秒;
循环外包含 try-catch 的平均执行时间是 630 纳秒,上下误差 38 纳秒。
也就是说,在没有发生异常的情况下,除去误差值,我们得到的结论是:try-catch 无论是在 for 循环内还是 for 循环外,它们的性能相同,几乎没有任何差别。


ry-catch的本质
要理解 try-catch 的性能问题,必须从它的字节码开始分析,只有这样我能才能知道 try-catch 的本质到底是什么,以及它是如何执行的。

此时我们写一个最简单的 try-catch 代码:

public class AppTest {
public static void main(String[] args) {
try {
int count = 0;
throw new Exception("new Exception");
} catch (Exception e) {
e.printStackTrace();
}
}
}
复制代码
然后使用 javac 生成字节码之后,再使用 javap -c AppTest 的命令来查看字节码文件:

➜ javap -c AppTest
警告: 二进制文件AppTest包含com.example.AppTest
Compiled from "AppTest.java"
public class com.example.AppTest {
public com.example.AppTest();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object.""

推荐阅读