Java异常的性能分析

在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这些方法。为了性能考虑,你应当手动解析它们。

特别说明:尊重作者的劳动成果,转载请注明出处哦~~~http://blog.yemou.net/article/query/info/tytfjhfascvhzxcyt276

时间: 2024-09-25 21:37:33

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

浅谈JAVA 异常对于性能的影响_java

在对客户做技术支持时,我们常常会看到很多客户根本没意识到的异常.在消除了这些异常之后,代码运行速度与以前相比大幅提升.这让我们产生一种猜测,就是在代码里面使用异常会带来显著的性能开销.因为异常是错误情况处理的重要组成部分,摒弃是不太可能的,所以我们需要衡量异常处理对于性能影响,我们可以通过一个实验看看异常处理的对于性能的影响. 实验 我的实验基于一段随机抛出异常的简单代码.从科学的角度,这并非完全准确的测量,同时我也并不了解HotSpot 编译器会对运行中的代码做何动作.但无论如何,这段代码应该

JAVA 异常对于性能的影响

在对OneAPM的客户做技术支 持时,我们常常会看到很多客户根本没意识到的异常.在消除了这些异常之后,代码运行速度与以前相比大幅提升.这让我们产生一种猜测,就是在代码里面使用异 常会带来显著的性能开销.因为异常是错误情况处理的重要组成部分,摒弃是不太可能的,所以我们需要衡量异常处理对于性能影响,我们可以通过一个实验看看异 常处理的对于性能的影响. 实验 我的实验基于一段随机抛出异常的简单代码.从科学的角度,这并非完全准确的测量,同时我也并不了解HotSpot 编译器会对运行中的代码做何动作.但无

JAVA异常是否对于性能有影响

  在对OneAPM的客户做技术支持时,我们常常会看到很多客户根本没意识到的异常.在消除了这些异常之后,代码运行速度与以前相比大幅提升.这让我们产生一种猜测,就是在代码里面使用异 常会带来显著的性能开销.因为异常是错误情况处理的重要组成部分,摒弃是不太可能的,所以我们需要衡量异常处理对于性能影响,我们可以通过一个实验看看异常处理的对于性能的影响. 实验 我的实验基于一段随机抛出异常的简单代码.从科学的角度,这并非完全准确的测量,同时我也并不了解HotSpot 编译器会对运行中的代码做何动作.但无

在NetBeans IDE 6.0中分析Java应用程序性能

NetBeans IDE 6.0 包含一个强大的性能分析工具,可提供与应用程序运行时行为有关的重要信息.通过 NetBeans 性能分析工具,我们可以方便地在 IDE 中监控应用程序的线程状态.CPU 性能以及内存使用情况,而且产生的开销相对较少. 本文将概述 IDE 中包含的性能分析工具,并指导您快速开始分析 NetBeans 项目的性能.本文旨在演示 IDE 中可用的各种性能分析任务以及分析项目性能时可以获得的分析结果.但并不覆盖 IDE 中包含的所有性能分析功能,也不会深入探索如何研究性能

java异常机制分析_java

本文实例分析了Java的异常机制,分享给大家供大家参考.相信有助于大家提高大家Java程序异常处理能力.具体分析如下: 众所周知,java中的异常(Exception)机制很重要,程序难免会出错,异常机制可以捕获程序中的错误,用来提高程序的稳定性和健壮性. java中的异常分为Checked Exception(非Runtime Exception)和UnChecked Exception(Runtime Exception),所有的异常类都直接或间接的继承Exception.Exception

Java遍历集合方法分析(实现原理、算法性能、适用场合)_javascript技巧

概述 Java语言中,提供了一套数据集合框架,其中定义了一些诸如List.Set等抽象数据类型,每个抽象数据类型的各个具体实现,底层又采用了不同的实现方式,比如ArrayList和LinkedList. 除此之外,Java对于数据集合的遍历,也提供了几种不同的方式.开发人员必须要清楚的明白每一种遍历方式的特点.适用场合.以及在不同底层实现上的表现.下面就详细分析一下这一块内容. 数据元素是怎样在内存中存放的? 数据元素在内存中,主要有2种存储方式: 1.顺序存储,Random Access(Di

Java性能分析点滴

性能测试工具点滴 中说明了性能测试的工具选择,如果软件存在性能问题,我们改如何解决呢?此时一些性能分析工工具会起到作用. 名词 http://en.wikipedia.org/wiki/Profiling_(computer_programming) http://zh.wikipedia.org/wiki/%E6%80%A7%E8%83%BD%E5%88%86%E6%9E%90 性能分析的原则:80-20,80%的问题集中于20%的代码中 CPU :程序效率 内存 :泄露和GC等情况 监控等几

Java 集合系列08之 List总结(LinkedList, ArrayList等使用场景和性能分析)

概要 前面,我们学完了List的全部内容(ArrayList, LinkedList, Vector, Stack). Java 集合系列03之 ArrayList详细介绍(源码解析)和使用示例  Java 集合系列04之 fail-fast总结(通过ArrayList来说明fail-fast的原理.解决办法)  Java 集合系列05之 LinkedList详细介绍(源码解析)和使用示例 Java 集合系列06之 Vector详细介绍(源码解析)和使用示例 Java 集合系列07之 Stack

通过日志监控并收集Java应用程序性能数据

引言 系统日志是应用程序问题诊断及运行维护的重要工具.Logback.Log4j 是常用于 Java 平台的日志记录 API. 目前大部分产品只是将系统重要参数.状态的变化及异常信息通过日志输出.本文将要介绍的 Perf4j 是一款专门用 于 Java 服务器端代码计时.记录日志和监控结果的开源工具包.Perf4j 对常用日志工具包进行了扩展,能够将得到的原 始性能数据进行统计并发布到可定制的输出源,如控制台.日志文件.JMX 等.Perf4j 提供了多种方式与 Java 代码集成 ,开发和系统