美文网首页
异常是耗时操作,用坏了整个系统就不好了

异常是耗时操作,用坏了整个系统就不好了

作者: 等你足够强了再说吧 | 来源:发表于2018-09-30 11:23 被阅读0次

软件工程领域的大师级人物 Robert C. Martin在《Clean Code》中讲道:
错误处理是十分必要的,但是如果对错误处理使用不当则会让代码变得十分臃> > 肿,让阅读者看不清代码的逻辑,更严重的是,这也会让程序变得十分脆弱。

使用Exception而不是返回码

返回码是一个历史遗留问题,在以前的没有Exception的语言(比如c语言)中,它是有效且必要的,但是在有Exception的语言中使用返回码是没有任何益处的。
对于使用返回码的函数,调用者在得到调用结果(这里是返回码)之后要立即去验证返回码,这对于代码的可读性和结构的合理性都是极大的挑战,使用「异常处理」能让业务逻辑和错误处理在代码结构上分离,代码的结构和逻辑会更清晰。

========华丽的分割线========

使用异常来取代层层传参来解决状态传递问题,的确是一种进步,但如果滥用,把本该使用返回值传参的地方也使用异常,得不偿失了,特别是TPS要求比较高的接口。

现在我们就来实测下这种抛异常的策略,是否会影响性能:
代码

/**
 * 相同条件下,同样的业务逻辑和IO下,比较抛异常和不抛异常场景下,
 * 在性能上有什么区别
 */
@Test
public void givenTwoScenario_whenOneHasExceptionAndAnotherNot_thenGetTheElapsedTime() {
    StopWatch stopWatch = new StopWatch("OneHasExceptionAndAnotherNot");
    stopWatch.start("hasNoException");
    int length = 1000000;
    for (int i = 0; i < length; i++) {
        doBizHasNoException(String.valueOf(i));
    }
    stopWatch.stop();

    stopWatch.start("hasException");
    for (int i = 0; i < length; i++) {
        doBizHasException(String.valueOf(i));
    }
    stopWatch.stop();
    log.info("{}", stopWatch.prettyPrint());
}

/**
 * 没有抛异常的场景【也打印个日志】
 *
 * @param i
 */
public void doBizHasNoException(String i) {
    try {
        log.info("result:{}", i);
    } catch (Exception e) {
        log.warn("{}", e.getMessage());
    }
}

/**
 * 抛异常的场景【打印个日志】
 *
 * @param i
 */
public void doBizHasException(String i) {
    try {
        throw new IllegalArgumentException(i);
    } catch (Exception e) {
        log.warn("{}", e.getMessage());
    }
}

实际执行效果:

10:19:26.327 [main] INFO com.tangcheng.learning.syntax.PerformanceAnalysisUseExceptionTest - StopWatch 'OneHasExceptionAndAnotherNot': running time (millis) = 17315
-----------------------------------------
ms     %     Task name
-----------------------------------------
05510  032%  hasNoException
11805  068%  hasException

可以看到,抛异常场景下,会更耗性能。
这个结论是不是很逆天,很不可思议。

当时也觉得百思不得其解,后来偶然在《极客时间》上听了 郑雨迪 讲的《深入拆解Java虚拟机》,感觉豁然开朗,那就不班门弄斧了,直接看看大佬来自jvm的视角:

异常实例的构造十分昂贵。这是由于在构造异常实例时,Java虚拟机便需要生成该异常的栈轨迹(stack trace)。该操作会逐一访问当前线程的Java栈帧,并且记录下各种调试信息,包括栈桢所指向方法的名字,方法所在的类名、文件名,以及在代码中的第几行触发该异常。

上面的文本不能copy,敲着比较费劲,那就直接截图了啊:

explain-1.jpg

评论永远有经典:

ask_1.jpg ask_2.jpg ask_3.jpg ask_4.jpg

这个栏目提供有试读,有兴趣的同学,来扫这个二维码:

2.jpg

相关文章

  • 异常是耗时操作,用坏了整个系统就不好了

    软件工程领域的大师级人物 Robert C. Martin在《Clean Code》中讲道:错误处理是十分必要的,...

  • APP启动优化,你应该没看到过这样子的

    一、检测APP启动耗时 用Android系统Debug API 生成文件,记录启动耗时操作。 用AndroidSt...

  • Hanlder异步消息自总结

    由于在UI线程中不能做耗时长的操作,所以系统提供了Handler和AsyncTask来进行异步消息处理和任务; 异...

  • 这是不是爱

    对你 时常是恨铁不成钢的感觉 看你们挺好的 我整个人就不好了

  • app卡顿优化相关

    常规套路: 使用AndroidPerformanceMonitor工具检测卡顿 检索代码的耗时操作,正确的迁移到异...

  • 「Android 学习计划」之线程池

    前言 由于 Android 系统不能在 UI 线程进行耗时的操作,通常我们会在子线程处理耗时操作。简单的写法就是就...

  • 在onTap事件中,执行耗时操作ui阻塞

    在onTap事件中,执行耗时操作ui阻塞,比如在onTap上传多张大照片。这里我们用sleep代替耗时操作 ui就...

  • android线程及线程池

    众所周知,在UI系统中进行一些耗时操作,都会导致卡顿现象,因为一次刷新在16ms,如果当次操作过了这个时间,那么用...

  • 耗时操作

    当我们在主线程执行一个循环量很大的操作的时候,这样会造成主线程的拥堵,这样的操作放在主线程会严重影响UI体验,比如...

  • Java的异常类Exception

    异常是程序中发生的错误事件,它破坏了程序指令的正常流程。 使用异常 try{}catch(){}可以捕获代码中的异...

网友评论

      本文标题:异常是耗时操作,用坏了整个系统就不好了

      本文链接:https://www.haomeiwen.com/subject/gkfwoftx.html