啪啪打脸!领导说:try-catch要放在循环体外!

这是我的第 206 期分享

作者 | 王磊

来源 | Java中文社群(ID:javacn666)

转载请联系授权(微信ID:GG_Stone)

哈喽,亲爱的小伙伴们,技术学磊哥,进步没得说!欢迎来到新一期的性能解读系列,我是磊哥。

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

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

小贴士:我会尽量用代码和评测结果来证明问题,但由于本身认知的局限,如有不当之处,请读者朋友们在评论区指出。

性能评测

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

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

<!-- https://mvnrepository.com/artifact/org.openjdk.jmh/jmh-core -->
<dependency><groupId>org.openjdk.jmh</groupId><artifactId>jmh-core</artifactId><version>{version}</version>
</dependency>

完整测试代码如下:

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(); // 执行测试}@Benchmarkpublic 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;}@Benchmarkpublic 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 循环外,它们的性能相同,几乎没有任何差别

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_01: invokespecial #1                  // Method java/lang/Object."<init>":()V4: returnpublic static void main(java.lang.String[]);Code:0: iconst_01: istore_12: new           #2                  // class java/lang/Exception5: dup6: ldc           #3                  // String new Exception8: invokespecial #4                  // Method java/lang/Exception."<init>":(Ljava/lang/String;)V11: athrow12: astore_113: aload_114: invokevirtual #5                  // Method java/lang/Exception.printStackTrace:()V17: returnException table:from    to  target type0    12    12   Class java/lang/Exception
}

从以上字节码中可以看到有一个异常表:

Exception table:from    to  target type0    12    12   Class java/lang/Exception

参数说明:

  • from:表示 try-catch 的开始地址;

  • to:表示 try-catch 的结束地址;

  • target:表示异常的处理起始位;

  • type:表示异常类名称。

从字节码指令可以看出,当代码运行时出错时,会先判断出错数据是否在 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 应该存放的位置,而非性能考虑

往期推荐

阿里巴巴为什么让初始化集合时必须指定大小?

局部变量竟然比全局变量快 5 倍?

关注公众号发送”进群“,磊哥拉你进读者群。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/546097.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

软件项目与产品的区别与联系_软件产品和软件过程之间的区别和关系

软件项目与产品的区别与联系软件产品和软件过程 (Software product and Software process) Software product and Software process: These two words are the one which is mostly confused with each other. In this article, we are going to explain each of these in deta…

Oracle官方推荐的性能测试工具!简单、精准又直观!

这是我的第 207 期分享作者 | 武培轩来源 | 武培轩&#xff08;ID&#xff1a;wupeixuan404&#xff09;分享 | Java中文社群&#xff08;ID&#xff1a;javacn666&#xff09;磊哥在前面的所有性能优化的文章中都是用了这款性能测试工具——JMH&#xff0c;一直没来得及给大家…

Hadoop开发第2期---虚拟机中搭建Linux

注&#xff1a;关于如何将hadoop源码导入Eclipse详见http://pan.baidu.com/s/1hq8ArUs 一、Hadoop配置软件&#xff08;我的电脑是Windows7旗舰--64bit&#xff09; 1. VMWare专用CentOS镜像(Centos是Linux操作系统的一种)2. VMware-workstation103. hadoop-1.1.2.tar.gz4. jdk…

看故事学知识,这篇Java代理的文章妙啊!

这是我的第 208 期分享作者 | java金融来源 | java金融&#xff08;ID&#xff1a;java4299&#xff09;分享 | Java中文社群&#xff08;ID&#xff1a;javacn666&#xff09;什么是代理代理模式是常用的java设计模式&#xff0c;他的特征是代理类与委托类有同样的接口&#x…

阿里《Java开发手册》中的 1 个bug!

这是我的第 210 期分享作者 | 王磊来源 | Java中文社群&#xff08;ID&#xff1a;javacn666&#xff09;转载请联系授权&#xff08;微信ID&#xff1a;GG_Stone&#xff09;本来打算写一篇《阿里巴巴为什么不允许日志输出时&#xff0c;使用字符串拼接&#xff1f;》的文章&a…

驳《阿里「Java开发手册」中的1个bug》?

这是我的第 211 期分享作者 | 王磊来源 | Java中文社群&#xff08;ID&#xff1a;javacn666&#xff09;转载请联系授权&#xff08;微信ID&#xff1a;GG_Stone&#xff09;前两天写了一篇关于《阿里Java开发手册中的 1 个bug》的文章&#xff0c;评论区有点炸锅了&#xff0…

轻松学算法的秘密!可视化算法网站汇总!(附动图)

对于「算法」的第一印象&#xff0c;我相信大部分人都是一样的&#xff0c;就是一个“难”字了得。而我比较特殊&#xff0c;我的第一印象、第二印象以至第 N 印象都觉得很难&#xff0c;所以为了更好的学习和理解算法&#xff0c;我千金一掷一下买了一堆的算法书&#xff0c;有…

链表竟然比数组慢了1000多倍?(动图+性能评测)

这是我的第 215 期分享作者 | 王磊来源 | Java中文社群&#xff08;ID&#xff1a;javacn666&#xff09;转载请联系授权&#xff08;微信ID&#xff1a;GG_Stone&#xff09;数组和链表是程序中常用的两种数据结构&#xff0c;也是面试中常考的面试题之一。然而对于很多人来说…

win7安装python

2019独角兽企业重金招聘Python工程师标准>>> 官网下载 python 安装包&#xff0c;一个 .msi 文件&#xff0c;双击安装 右键[计算机]-属性-高级系统设置-环境变量&#xff0c;在[系统变量]中找到 Path 变量&#xff0c;双击&#xff0c;在值后面添加 python 的安装路…

漫画:Java如何实现热更新?

Arthas&#xff08;阿尔萨斯&#xff09;是 Alibaba 开源的一款 Java 诊断工具&#xff0c;使用它我们可以监控和排查 Java 程序&#xff0c;然而它还提供了非常实用的 Java 热更新功能。所谓的 Java 热更新是指在不重启项目的情况下实现代码的更新与替换。使用它可以实现不停机…

自动化运维工具Saltstack详细介绍

Saltstack是一个新的基础设施管理工具。目前处于快速发展阶段&#xff0c;可以看做是pssh弱化的Puppet的组合。间接的反映出了saltstack的两大功能&#xff1a;远程执行和配置管理。Saltstack使用Python开发&#xff0c;是一个非常简单易用和轻量级的管理工具。由Master和Minio…

为什么建议你使用枚举?

枚举是 JDK 1.5 新增的数据类型&#xff0c;使用枚举我们可以很好的描述一些特定的业务场景&#xff0c;比如一年中的春、夏、秋、冬&#xff0c;还有每周的周一到周天&#xff0c;还有各种颜色&#xff0c;以及可以用它来描述一些状态信息&#xff0c;比如错误码等。枚举类型不…

数据结构树二叉树计算节点_查找二叉树中叶节点的数量 数据结构

数据结构树二叉树计算节点Algorithm: 算法&#xff1a; One of the popular traversal techniques to solve this kind of problems is level order tree traversal (Read: Level Order Traversal on a Binary Tree) where we use the concept of BFS. 解决此类问题的一种流行…

重磅!阿里推出国产开源JDK!

简介Alibaba Dragonwell 是一款免费的, 生产就绪型Open JDK 发行版&#xff0c;提供长期支持&#xff0c;包括性能增强和安全修复。阿里巴巴拥有最丰富的Java应用场景&#xff0c;覆盖电商&#xff0c;金融&#xff0c;物流等众多领域&#xff0c;世界上最大的Java用户之一。Al…

安装TPCC-MySQL报错

2019独角兽企业重金招聘Python工程师标准>>> 安装TPCC-MySQL做压力测试&#xff0c;由于TPCC-MySQL是bzr工具进行版本控制的&#xff0c;所以要先安装bzr [rootmha_backup /root] #rpm -Uvh http://dl.fedoraproject.org/pub/epel/5/i386/epel-release-5-4.noarch.r…

自由职业的前半年,我是如何度过的?

生活中所受的苦&#xff0c;终会以一种形式回归。我是一个后知后觉的人&#xff0c;从 2009 年毕业到现在&#xff0c;已经有 11 年的光景了&#xff0c;参加工作的前几年我从没想过要快速的提升自己的技能&#xff0c;对待工作也没有全力以赴&#xff0c;这样的迷茫和随大流的…

Eucalyptus常用查询命令

前言&#xff1a; Elastic Utility Computing Architecture for Linking Your Programs To Useful Systems &#xff08;Eucalyptus&#xff09; 是一种开源的软件基础结构&#xff0c;用来通过计算集群或工作站群实现弹性的、实用的云计算。它最初是美国加利福尼亚大学 Santa …

调整灰度图像的大小,而无需在Python中使用任何内置函数

In this program, we will be using two functions of OpenCV-python (cv2) module. Lets see their syntax and descriptions first. 在此程序中&#xff0c;我们将使用OpenCV-python(cv2)模块的两个功能。 首先让我们看看它们的语法和说明。 1) imread():It takes an absolu…

漫画:对象是如何被找到的?句柄 OR 直接指针?

小贴士&#xff1a;想要使用并定位 Java 对象&#xff0c;就要用到 Java 虚拟机栈&#xff08;Java Virtual Machine Stack&#xff09;&#xff0c;它描述的是 Java 方法执行的线程内存模型&#xff1a;每个方法被执行的时候&#xff0c;Java 虚拟机都会同步创建一个栈帧&…