• try - catch 语句真的会影响性能吗?


    不知道从何时起,传出了这么一句话:Java中使用try catch 会严重影响性能。然而,事实真的如此么?我们对try catch 应该畏之如猛虎么?

    一、JVM 异常处理逻辑

    Java 程序中显式抛出异常由athrow指令支持,除了通过 throw 主动抛出异常外,JVM规范中还规定了许多运行时异常会在检测到异常状况时自动抛出(效果等同athrow), 例如除数为0时就会自动抛出异常,以及大名鼎鼎的 NullPointerException 。

    还需要注意的是,JVM 中 异常处理的catch语句不再由字节码指令来实现(很早之前通过 jsr和 ret指令来完成,它们在很早之前的版本里就被舍弃了),现在的JVM通过异常表(Exception table 方法体中能找到其内容)来完成 catch 语句;很多人说try catch 影响性能可能就是因为认识还停留于上古时代。

    我们编写如下的类,add 方法中计算 ++x; 并捕获异常。

    1. public class TestClass {
    2. private static int len = 779;
    3. public int add(int x){
    4. try {
    5. // 若运行时检测到 x = 0,那么 jvm会自动抛出异常,(可以理解成由jvm自己负责 athrow 指令调用)
    6. x = 100/x;
    7. } catch (Exception e) {
    8. x = 100;
    9. }
    10. return x;
    11. }
    12. }

    使用javap 工具查看上述类的编译后的class文件

    1. # 编译
    2. javac TestClass.java
    3. # 使用javap 查看 add 方法被编译后的机器指令
    4. javap -verbose TestClass.class

    忽略常量池等其他信息,下边贴出add 方法编译后的 机器指令集:

    1. public int add(int);
    2. descriptor: (I)I
    3. flags: ACC_PUBLIC
    4. Code:
    5. stack=2, locals=3, args_size=2
    6. 0: bipush 100 // 加载参数100
    7. 2: iload_1 // 将一个int型变量推至栈顶
    8. 3: idiv // 相除
    9. 4: istore_1 // 除的结果值压入本地变量
    10. 5: goto 11 // 跳转到指令:11
    11. 8: astore_2 // 将引用类型值压入本地变量
    12. 9: bipush 100 // 将单字节常量推送栈顶<这里与数值100有关,可以尝试修改100后的编译结果:iconst、bipush、ldc>
    13. 10: istore_1 // 将int类型值压入本地变量
    14. 11: iload_1 // int 型变量推栈顶
    15. 12: ireturn // 返回
    16. // 注意看 fromto 以及 targer,然后对照着去看上述指令
    17. Exception table:
    18. from to target type
    19. 0 5 8 Class java/lang/Exception
    20. LineNumberTable:
    21. line 6: 0
    22. line 9: 5
    23. line 7: 8
    24. line 8: 9
    25. line 10: 11
    26. StackMapTable: number_of_entries = 2
    27. frame_type = 72 /* same_locals_1_stack_item */
    28. stack = [ class java/lang/Exception ]
    29. frame_type = 2 /* same */

    再来看 Exception table:

    from=0, to=5。指令 0~5 对应的就是 try 语句包含的内容,而targer = 8 正好对应 catch 语句块内部操作。

    • 个人理解,from 和 to 相当于划分区间,只要在这个区间内抛出了type 所对应的,“java/lang/Exception” 异常(主动athrow 或者 由jvm运行时检测到异常自动抛出),那么就跳转到target 所代表的第八行。

    若执行过程中,没有异常,直接从第5条指令跳转到第11条指令后返回,由此可见未发生异常时,所谓的性能损耗几乎不存在;

    • 如果硬是要说的话,用了try catch 编译后指令篇幅变长了;goto 语句跳转会耗费性能,当你写个数百行代码的方法的时候,编译出来成百上千条指令,这时候这句goto的带来的影响显得微乎其微。如图所示为去掉try catch 后的指令篇幅,几乎等同上述指令的前五条。

    综上所述:“Java中使用try catch 会严重影响性能” 是民间说法,它并不成立。如果不信,接着看下面的测试吧。

    二、关于JVM的编译优化

    其实写出测试用例并不是很难,这里我们需要重点考虑的是编译器的自动优化,是否会因此得到不同的测试结果?

    本节会粗略的介绍一些jvm编译器相关的概念,讲它只为更精确的测试结果,通过它我们可以窥探 try catch 是否会影响JVM的编译优化。

    前端编译与优化:我们最常见的前端编译器是 javac,它的优化更偏向于代码结构上的优化,它主要是为了提高程序员的编码效率,不怎么关注执行效率优化;例如,数据流和控制流分析、解语法糖等等。

    后端编译与优化:后端编译包括 “即时编译[JIT]” 和 “提前编译[AOT]”,区别于前端编译器,它们最终作用体现于运行期,致力于优化从字节码生成本地机器码的过程(它们优化的是代码的执行效率)。

    1. 分

  • 相关阅读:
    针对JavaScript混淆加密,JShaman推出新功能
    JAVA进阶之旅(一)——增强for循环,基本数据类型的自动拆箱与装箱,享元设计模式,枚举的概述,枚举的应用
    【快应用】如何使用命令打包快应用rpk
    CAx软件中若干C++日志库
    MySQL——连接查询
    爬手机app接口数据,手机不能上网问题及解决,
    基础gdb操作【Linux】
    [PAT-Advanced] B1019/A1069. The Black Hole of Numbers (20)
    怎样做一个好的汇报
    web前端网页设计期末课程大作业:关于城市旅游的HTML网页设计 ——北京
  • 原文地址:https://blog.csdn.net/Q54665642ljf/article/details/126586377