try catch两种情况的性能对比(放在循环内和放在循环外)

先说结论
在没有发生异常时,两者性能上没有差异。如果发生异常,两者的处理逻辑不一样,已经不具有比较的意义了。

错误测试代码:

import java.util.ArrayList;

/**
 * @author: lzq
 * @description: MyTest
 * @date: 2021/11/5 10:27 上午
 */
public class MyTest {
    public static void main(String[] args)
    {
        int size = 10000000;

        method1(size);
        method2(size);
        method3(size);
    }
    public static void method1(int size)
    {
        long start = System.currentTimeMillis();
        ArrayList<String> al = new ArrayList<String>();
        String str = null;
        try
        {
            for (int i = 0; i < size; i++)
            {
                str = "str" + i;
                al.add(str);
            }
        }
        catch (Exception e)
        {
        }
        System.out.println("method1 total: " + (System.currentTimeMillis() - start));
    }
    public static void method2(int size)
    {
        long start = System.currentTimeMillis();
        ArrayList<String> al = new ArrayList<String>();
        String str = null;
        for (int i = 0; i < size; i++)
        {
            try
            {
                str = "str" + i;
                al.add(str);
            }
            catch (Exception e)
            {
            }
        }
        System.out.println("method2 total: " + (System.currentTimeMillis() - start));
    }
    public static void method3(int size)
    {
        long start = System.currentTimeMillis();
        ArrayList<String> al = new ArrayList<String>();
        String str = null;
        for (int i = 0; i < size; i++)
        {
            str = "str" + i;
            al.add(str);
        }
        System.out.println("method3 total: " + (System.currentTimeMillis() - start));
    }
}

结果会发现时快时慢,看不出明显结论
在这里插入图片描述
在这里插入图片描述
原因:

  1. System.currentTimeMillis()测量的只是逝去的时间,并没有反映出cpu执行该函数真正消耗的时间。
  2. 这导致线程未被分配cpu资源时,等待cpu的时间也会被计算进去 JIT优化导致结果出现偏差。
  3. 类加载时间也被统计进来了。 类首次被使用时,会触发类加载,产生了时间消耗。

像这种多次循环非常容易触发JIT的优化机制,关于JIT,这里简短的介绍一下

在Java编程语言和环境中,即时编译器(JIT compiler,just-in-time compiler)是一个把Java的字节码(包括需要被解释的指令的程序)转换成可以直接发送给处理器的指令的程序。当你写好一个Java程序后,源语言的语句将由Java编译器编译成字节码,而不是编译成与某个特定的处理器硬件平台对应的指令代码(比如,Intel的Pentium微处理器或IBM的System/390处理器)。字节码是可以发送给任何平台并且能在那个平台上运行的独立于平台的代码。

JIT 编译器概述
Just-In-Time (JIT) 编译器是 Java™ Runtime Environment 的一个组件,用于提高运行时的 Java
应用程序的性能。

Java 程序由多个类组成,它包含可在许多不同计算机体系结构上由 JVM 解释的与平台无关的字节码。在运行时,JVM 装入类文件,确定每个单独的字节码的语义,并执行相应的计算。解释期间额外使用处理器和内存意味着 Java应用程序的执行速度要慢于本机应用程序。JIT 编译器通过在运行时将字节码编译为本机代码以帮助提高 Java 程序的性能。

JIT 编译器在缺省情况下为已启用,并在调用 Java 方法时被激活。JIT 编译器将该方法的字节码编译为本机机器码,“即时”编译该代码以便运行。在编译方法时,JVM 直接调用该方法的已编译代码,而不是对代码进行解释。理论上,如果编译不需要占用处理器时间和内存,那么编译每个方法都可能使 Java程序速度接近于本机应用程序的速度。

JIT 编译需要占用处理器时间和内存。在 JVM首次启动时,将调用数千种方法。即使程序最终实现了较高的峰值性能,编译所有这些方法也会对启动时间产生显著影响。

实际上,第一次调用方法时不会对方法进行编译。 对于每个方法,JVM 都会保留一个调用计数,每次调用方法时该计数都将递增。JVM对方法进行解释,直至其调用计数超过 JIT 编译阈值。因此,在 JVM启动后将立即编译常用方法,而在较长时间之后(或者根本不编译)不常使用的方法。JIT 编译阈值帮助 JVM 快速启动并且还可提高性能。 谨慎选择阈值,以在启动时与长期性能之间实现最佳平衡。

在编译方法后,调用计数将重置为 0,并且对该方法的后续调用将继续使其计数递增。在方法的调用计数到达 JIT 重新编译阈值时,JIT 编译器将执行第二次编译,与前一次编译相比,其优化选择更多。此过程将重复,直至到达最大优化级别。Java程序的最忙碌方法始终是最积极地进行优化,从而实现使用 JIT 编译器的性能优势最大化。JIT编译器还可在运行时度量运作数据,并使用该数据来提高进一步重新编译的质量。

可禁用 JIT 编译器,在这种情况下,将解释整个 Java 程序。除诊断或解决 JIT 编译问题外,不推荐禁用 JIT 编译器。

结论扩充:
try catch与未使用try catch代码区别在于,前者阻止Java对try块的代码的一些优化,例如重排序。try catch里面的代码是不会被编译器优化重排的。对于上面两个函数而言,只是异常表中try起点和终点位置不一样。至于刚刚说到的指令重排的问题,由于for循环条件部分符合happens- before原则,因此两者的for循环都不会发生重排。当然只是针对这里而言,在实际编程中,还是提倡try代码块的范围尽量小,这样才可以充分发挥Java对代码的优化能力。

最终总结:
本文由Try catch与for循环的位置关系开始讨论,通过分析得出了结论,并最终通过测试,验证了分析的结论——两者在没有抛出异常时,是没有区别的。在分析的过程中,我们也了解到try catch的实质,就是跟方法关联的异常表,在抛出异常的时候,这个就决定了异常是否会被该方法处理。

最后回到标题讨论的,try catch对性能的影响。try catch对性能还是有一定的影响,那就是try块会阻止java的优化(例如重排序)。当然重排序是需要一定的条件触发。一般而言,只要try块范围越小,对java的优化机制的影响是就越小。所以保证try块范围尽量只覆盖抛出异常的地方,就可以使得异常对java优化的机制的影响最小化。

还是那句话,先保证代码正确执行,然后在出现明显的性能问题时,再去考虑优化。

参考:https://blog.csdn.net/lylwo317/article/details/51869893

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值