今天给大家带来的是关于 try-catch 应该放在循环体外,还是放在循环体内的文章,我们将从性能和业务场景分析这两个方面来回答此问题。
主要从事网页设计、PC网站建设(电脑版网站建设)、wap网站建设(手机版网站建设)、成都响应式网站建设公司、程序开发、微网站、重庆小程序开发公司等,凭借多年来在互联网的打拼,我们在互联网网站建设行业积累了丰富的成都网站设计、成都网站建设、网络营销经验,集策划、开发、设计、营销、管理等多方位专业化运作于一体,具备承接不同规模与类型的建设项目的能力。
很多人对 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 无论是在 for 循环内还是 for 循环外,它们的性能相同,几乎没有任何差别。
try-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."
":()V - 4: return
- public static void main(java.lang.String[]);
- Code:
- 0: iconst_0
- 1: istore_1
- 2: new #2 // class java/lang/Exception
- 5: dup
- 6: ldc #3 // String new Exception
- 8: invokespecial #4 // Method java/lang/Exception."
":(Ljava/lang/String;)V - 11: athrow
- 12: astore_1
- 13: aload_1
- 14: invokevirtual #5 // Method java/lang/Exception.printStackTrace:()V
- 17: return
- Exception table:
- from to target type
- 0 12 12 Class java/lang/Exception
- }
从以上字节码中可以看到有一个异常表:
- Exception table:
- from to target type
- 0 12 12 Class java/lang/Exception
参数说明:
从字节码指令可以看出,当代码运行时出错时,会先判断出错数据是否在 from 到 to 的范围内,如果是则从 target 标志位往下执行,如果没有出错,直接 goto 到 return。也就是说,如果代码不出错的话,性能几乎是不受影响的,和正常的代码的执行逻辑是一样的。
业务情况分析
虽然 try-catch 在循环体内还是循环体外的性能是类似的,但是它们所代码的业务含义却完全不同,例如以下代码:
- public class AppTest {
- public static void main(String[] args) {
- System.out.println("循环内的执行结果:" + innerForeach());
- System.out.println("循环外的执行结果:" + outerForeach());
- }
- // 方法一
- public static int innerForeach() {
- int count = 0;
- for (int i = 0; i < 6; i++) {
- try {
- if (i == 3) {
- throw new Exception("new Exception");
- }
- count++;
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- return count;
- }
- // 方法二
- public static int outerForeach() {
- int count = 0;
- try {
- for (int i = 0; i < 6; i++) {
- if (i == 3) {
- throw new Exception("new Exception");
- }
- count++;
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
- return count;
- }
- }
以上程序的执行结果为:
- java.lang.Exception: new Exception
- at com.example.AppTest.innerForeach(AppTest.java:15)
- at com.example.AppTest.main(AppTest.java:5)
- java.lang.Exception: new Exception
- at com.example.AppTest.outerForeach(AppTest.java:31)
- at com.example.AppTest.main(AppTest.java:6)
- 循环内的执行结果:5
- 循环外的执行结果:3
可以看出在循环体内的 try-catch 在发生异常之后,可以继续执行循环;而循环外的 try-catch 在发生异常之后会终止循环。
因此我们在决定 try-catch 究竟是应该放在循环内还是循环外,不取决于性能(因为性能几乎相同),而是应该取决于具体的业务场景。
例如我们需要处理一批数据,而无论这组数据中有哪一个数据有问题,都不能影响其他组的正常执行,此时我们可以把 try-catch 放置在循环体内;而当我们需要计算一组数据的合计值时,只要有一组数据有误,我们就需要终止执行,并抛出异常,此时我们需要将 try-catch 放置在循环体外来执行。
总结
本文我们测试了 try-catch 放在循环体内和循环体外的性能,发现二者在循环很多次的情况下性能几乎是一致的。然后我们通过字节码分析,发现只有当发生异常时,才会对比异常表进行异常处理,而正常情况下则可以忽略 try-catch 的执行。但在循环体内还是循环体外使用 try-catch,对于程序的执行结果来说是完全不同的,因此我们应该从实际的业务出发,来决定到 try-catch 应该存放的位置,而非性能考虑。
新闻标题:啪啪打脸!领导说:try-catch要放在循环体外!
地址分享:http://www.csdahua.cn/qtweb/news48/238398.html
网站建设、网络推广公司-快上网,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等
声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 快上网