详见: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. Python获取股票历史、实时数据与更新到数据库

    要做量化投资,数据是基础,正所谓"巧妇难为无米之炊" 在免费数据方面,各大网站的财经板块其实已提供相应的api,如新浪.雅虎.搜狐...可以通过urlopen相应格式的网址获取数据 ...

  2. 【原创】-- C# 点滴积累 -- String

    一.string.Format() 将[数字字符串]转为两位小数显示的字符串: dt = dtResult.Select(it => new CommodityPriceLimitEntity ...

  3. 分享 C++图像处理的代码简易示例

    采用Decoder:stb_image https://github.com/nothings/stb/blob/master/stb_image.h 采用Encoder:tiny_jpeghttps ...

  4. 【Java学习笔记之八】java二维数组及其多维数组的内存应用拓展延伸

    多维数组声明 数据类型[][] 数组名称; 数据类型[] 数组名称[]; 数据类型数组名称[][]; 以上三种语法在声明二维数组时的功能是等价的.同理,声明三维数组时需要三对中括号,中括号的位置可以在 ...

  5. 使用yeoman构建angular应用

    本文将介绍如何亲手来完成一个yeoman的generator,以实现快速构建最适合自己的项目. 本文将实现的generator起名为ngtimo,依照yeoman的命名规矩就叫做generator-n ...

  6. 【HTTP权威指南】第三章-HTTP报文

    HTTP是因特网的信使,报文就是信使运送的包裹. 这一章包含: 报文如何流动 报文的三个组成部分(起始行,首部,实体的主体部分) 请求报文和响应报文的区别 请求报文支持的各种功能(方法) 响应报文返回 ...

  7. 【Spring】浅谈ContextLoaderListener及其上下文与DispatcherServlet的区别

    一般在使用SpingMVC开发的项目中,一般都会在web.xml文件中配置ContextLoaderListener监听器,如下: <listener> <listener-clas ...

  8. cornerstone 使用报错 working copy ... is too old(format 10 created by subversion 1.6)

    1.....本来正常使用的cornerstone 突然出现这个问题 图片如下 2....解决方案,,,,也搜集了方法,,但是最后竟然就这样解决了,,完全搞不懂的解决方案啊 如图 3......如有错误 ...

  9. CSS规范--春风十里不如写好CSS

    先吟几句: 最近看了看春风十里不如你,本来很少看剧的,暑假有点闲就看了,感觉不错,挺喜欢这部剧,就套了个名字,嘿嘿嘿.剧里面印象深刻的是<致橡树>这首诗,念几句: 我如果爱你,绝不像攀援的 ...

  10. 如何验证所做的AIX系统备份是否可用

    --如何验证所做的AIX系统备份是否可用 ----------------------------------2013/11/15 系统备份(mksysb)的介质可以是磁带,也可以是CD和DVD.想要 ...