详见:http://blog.yemou.net/article/query/info/tytfjhfascvhzxcyt276

在Java中抛异常的性能是非常差的。通常来说,抛一个异常大概会消耗100到1000个时钟节拍。

通常是出现了意想不到的错误,我们才会往外抛异常。也就是说,我们肯定不希望一个进程一秒钟就抛出上千个异常。不过有时候你确实会碰到有些方法把异常当作事件一样往外抛。我们在这篇文章中已经看到一个这样的典范):sun.misc.BASE64Decoder之所以性能很差就是因为它通过抛异常来对外请求道,”我还需要更多的数据“:

1
2
3
4
5
6
7
8
9
10
at java.lang.Throwable.fillInStackTrace(Throwable.java:-1)
at java.lang.Throwable.fillInStackTrace(Throwable.java:782)
- locked <0x6c> (a sun.misc.CEStreamExhausted)
at java.lang.Throwable.<init>(Throwable.java:250)
at java.lang.Exception.<init>(Exception.java:54)
at java.io.IOException.<init>(IOException.java:47)
at sun.misc.CEStreamExhausted.<init>(CEStreamExhausted.java:30)
at sun.misc.BASE64Decoder.decodeAtom(BASE64Decoder.java:117)
at sun.misc.CharacterDecoder.decodeBuffer(CharacterDecoder.java:163)
at sun.misc.CharacterDecoder.decodeBuffer(CharacterDecoder.java:194)

如果你用一个数字开头,字母结尾的字符串来运行下这篇文章里面的pack方法,你也会碰到类似的情况。我们来看下用那个方法打包"12345"和"12345a"需要多长的时间:

1
2
Made 100.000.000 iterations for string '12345' : time = 12.109 sec
Made 1.000.000 iterations for string '12345a' : time = 21.764 sec

可以看到,’12345a'迭代的次数要比‘12345’少100倍。也就是说这个方法处理'12345a'慢了差不多200倍。大多数的处理时间都在填充异常的栈跟踪信息了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
at java.lang.Throwable.fillInStackTrace(Throwable.java:-1)
        at java.lang.Throwable.fillInStackTrace(Throwable.java:782)
        - locked <0x87> (a java.lang.NumberFormatException)
        at java.lang.Throwable.<init>(Throwable.java:265)
        at java.lang.Exception.<init>(Exception.java:66)
        at java.lang.RuntimeException.<init>(RuntimeException.java:62)
        at java.lang.IllegalArgumentException.<init>(IllegalArgumentException.java:53)
        at java.lang.NumberFormatException.<init>(NumberFormatException.java:55)
        at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
        at java.lang.Long.parseLong(Long.java:441)
        at java.lang.Long.valueOf(Long.java:540)
        at com.mvorontsov.javaperf.StrConvTests.pack(StrConvTests.java:69)
        at com.mvorontsov.javaperf.StrConvTests.test(StrConvTests.java:38)
        at com.mvorontsov.javaperf.StrConvTests.main(StrConvTests.java:29)

通过手动解析数字,我们可以很容易提升pack方法的性能。不过不要忘了——不到万不得已,不要随便优化。如果你只是解析几个输入参数而已—— keep it simple,就用JDK的方法就好了。如果你要解析大量的消息,又必须调用一个类似pack这样的方法——那确实得去优化一下了。

新的pack方法和旧的实现差不太多——把一个字符串转化成一个尽可能小的Character/Integer/Long/Double/String类型,使得result.toString().equals(orginalString)为true。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
public static Object strToObject( final String str )
{
    if ( str == null || str.length() > 17 )
    {  //out of Long range
        return str;
    }
    if ( str.equals( "" ) )
        return ""; //ensure interned string is returned
    if ( str.length() == 1 )
        return str.charAt( 0 ); //return Character
    //if starts with zero - support only "0" and "0.something"
    if ( str.charAt( 0 ) == '0' )
    {
        if ( str.equals( "0" ) )
            return 0;
        if ( !str.startsWith( "0." ) )  //this may be a double
            return str;
    }
  
    long res = 0;
    int sign = 1;
    for ( int i = 0; i < str.length(); ++i )
    {
        final char c = str.charAt( i );
        if ( c <= '9' && c >= '0' )
            res = res * 10 + ( c - '0' );
        else if ( c == '.' )
        {
            //too lazy to write a proper Double parser, use JDK one
            try
            {
                final Double val = Double.valueOf( str );
                //check if value converted back to string equals to an original string
                final String reverted = val.toString();
                return reverted.equals( str ) ? val : str;
            }
            catch ( NumberFormatException ex )
            {
                return str;
            }
        }
        else if ( c == '-' )
        {
            if ( i == 0 )
                sign = -1; //switch sign at first position
            else
                return str; //otherwise it is not numeric
        }
        else if ( c == '+' )
        {
            if ( i == 0 )
                sign = 1; //sign at first position
            else
                return str; //otherwise it is not numeric
        }
        else //non-numeric
            return str;
    }
    //cast to int if value is in int range
    if ( res < Integer.MAX_VALUE )
        return ( int ) res * sign;
    //otherwise return Long
    return res * sign;
}

很惊讶吧,新的方法解析数字比JDK的实现快多了!很大一个原因是因为JDK在解析的最后,调用了一个支持的解析方法,像这样:

public static int parseInt( String s, int radix ) throws NumberFormatException

新的方法和旧的相比(注意方法调用的次数——对于非数字串pack只调用了1百万次,而别的情况能调用到千万级别):

1
2
3
4
Pack: Made 100.000.000 iterations for string '12345' : time = 12.145 sec
Pack: Made 1.000.000 iterations for string '12345a' : time = 23.248 sec
strToObject: Made 100.000.000 iterations for string '12345' : time = 6.311 sec
strToObject: Made 100.000.000 iterations for string '12345a' : time = 5.807 sec

总结

千万不要把异常当成返回码一样用,或者当作可能发生的事件(尤其是和IO无关的方法)往外抛。抛异常的代价太昂贵了,对于一般的方法,至少要慢百倍以上。

如果你每条数据都需要解析,又经常会出现非数值串的时候,尽量不要用Number子类型的parse*/valueOf这些方法。为了性能考虑,你应当手动解析它们。

Java异常的性能分析的更多相关文章

  1. Java应用常用性能分析工具

    Java应用常用性能分析工具 好的工具有能有效改善和提高工作效率或加速分析问题的进度,笔者将从事Java工作中常用的性能工具和大家分享下,如果感觉有用记得投一票哦,如果你有好的工具也可以分享给我 工具 ...

  2. JAVA 异常对于性能的影响

    陶炳哲 - MAY 12, 2015 在对OneAPM的客户做技术支持时,我们常常会看到很多客户根本没意识到的异常.在消除了这些异常之后,代码运行速度与以前相比大幅提升.这让我们产生一种猜测,就是在代 ...

  3. Java几款性能分析工具的对比

    在给客户的应用程序维护的过程中,我注意到在高负载下的一些性能问题.理论上,增加对应用程序的负载会使性能等比率的下降.然而,我认为性能下降的比率远远高于负载的增加.我也发现,性能可以通过改变应用程序的逻 ...

  4. [转]Java Thread Dump 性能分析

    Java and Thread 一个 web 服务器使用几十到几百个线程来处理大量并发用户,如果一个或多个线程使用相同的资源,线程之间的竞争就不可避免了,并且有时候可能会发生死锁. Thread co ...

  5. 使用JDK自带的VisualVM进行Java程序的性能分析

    VisualVM是什么? VisualVM是JDK自带的一个用于Java程序性能分析的工具,JDK安装完毕后就有啦,在JDK安装目录的bin文件夹下能找到名称为jvisualvm.exe. 要使用Vi ...

  6. Java 性能分析工具 , 第 1 部分: 操作系统工具

    引言 性能分析的前提是将应用程序内部的运行状况以及应用运行环境的状况以一种可视化的方式更加直接的展现出来,如何来达到这种可视化的展示呢?我们需要配合使用操作系统中集成的程序监控工具和 Java 中内置 ...

  7. Java 性能分析工具 , 第 3 部分: Java Mission Control

    引言 本文为 Java 性能分析工具系列文章第三篇,这里将介绍如何使用 Java 任务控制器 Java Mission Control 深入分析 Java 应用程序的性能,为程序开发人员在使用 Jav ...

  8. 软件性能测试分析与调优实践之路-Java应用程序的性能分析与调优-手稿节选

    Java编程语言自从诞生起,就成为了一门非常流行的编程语言,覆盖了互联网.安卓应用.后端应用.大数据等很多技术领域,因此Java应用程序的性能分析和调优也是一门非常重要的课题.Java应用程序的性能直 ...

  9. Java 性能分析工具 , 第 2 部分:Java 内置监控工具

    引言 本文为 Java 性能分析工具系列文章第二篇,第一篇:操作系统工具.在本文中将介绍如何使用 Java 内置监控工具更加深入的了解 Java 应用程序和 JVM 本身.在 JDK 中有许多内置的工 ...

随机推荐

  1. 【整理】01. jQuery.Form.js 用法分析

    前言: 为什么使用JQuery.From.js,因为原生<form>表单没有回调函数,不过可以通过jquery-form.js这个插件来实现回调函数. jQuery.form.js是一个f ...

  2. RabbitMQ入门-高效的Work模式

    扛不住的Hello World模式 上篇<RabbitMQ入门-从HelloWorld开始>介绍了RabbitMQ中最基本的Hello World模型.正如其名,Hello World模型 ...

  3. VM虚拟机中安装Linux操作系统

    本文操作步骤,笔者已实验成功 (前提:正确安装VM并激活) 1,点击新建虚拟机,在页面上选择"自定义",点击下一步 2,进入选择虚拟机硬件兼容页面,这里一般不用操作,直接点击下一步 ...

  4. 【 js 基础 】【 源码学习 】backbone 源码阅读(二)

    最近看完了 backbone.js 的源码,这里对于源码的细节就不再赘述了,大家可以 star 我的源码阅读项目(source-code-study)进行参考交流,有详细的源码注释,以及知识总结,同时 ...

  5. python关于list的三个内置函数filter(), map(), reduce()

    ''' Python --version :Python 2.7.11 Quote : https://docs.python.org/2/tutorial/datastructures.html#m ...

  6. (5)UIView常见属性

    此时打印的所有子控件会把使用自动布局的控件也打印出来,不准确,所以得去掉这两个选项,再进行打印 使用实例如下: viewWithTag的注意点,当有多个相同的Tag值时,它是先找到第一个Tag值,而不 ...

  7. 关于Java JDK中 URLDecoder.decode 方法

    java.net.URLDecoder.decode 在项目中碰到了个比较奇怪的问题,就是我在本地使用java.net.URLDecoder.decode(ruleName)方法解码,没有问题,本地的 ...

  8. HTML的学习笔记

    一.HTML的概述(了解) a.html是什么 : hypertext markup language 超文本标记语言           超文本:音频,视频,图片称为超文本..           ...

  9. [NOIP2015] 斗地主 大爆搜

    考试的时候想了半天,实在是想不到解决的办法,感觉只能暴力..然后暴力也懒得打了,小数据模拟骗30分hhh 然而正解真的是暴力..大爆搜.. 然后我的内心拒绝改这道题(TAT) 不过在wcx大佬的帮助下 ...

  10. 一个普通的 Zepto 源码分析(三) - event 模块

    一个普通的 Zepto 源码分析(三) - event 模块 普通的路人,普通地瞧.分析时使用的是目前最新 1.2.0 版本. Zepto 可以由许多模块组成,默认包含的模块有 zepto 核心模块, ...