异常表 java 性能_Java异常的性能分析

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

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

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.(Throwable.java:250)

at java.lang.Exception.(Exception.java:54)

at java.io.IOException.(IOException.java:47)

at sun.misc.CEStreamExhausted.(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"需要多长的时间:

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倍。大多数的处理时间都在填充异常的栈跟踪信息了:

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.(Throwable.java:265)

at java.lang.Exception.(Exception.java:66)

at java.lang.RuntimeException.(RuntimeException.java:62)

at java.lang.IllegalArgumentException.(IllegalArgumentException.java:53)

at java.lang.NumberFormatException.(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。

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百万次,而别的情况能调用到千万级别):

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异常的性能分析

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值