详见: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 )
{
return
str;
}
if
( str.equals(
""
) )
return
""
;
if
( str.length() == 1 )
return
str.charAt( 0 );
if
( str.charAt( 0 ) ==
'0'
)
{
if
( str.equals(
"0"
) )
return
0;
if
( !str.startsWith(
"0."
) )
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 ==
'.'
)
{
try
{
final Double val = Double.valueOf( str );
final String reverted = val.toString();
return
reverted.equals( str ) ? val : str;
}
catch
( NumberFormatException ex )
{
return
str;
}
}
else
if
( c ==
'-'
)
{
if
( i == 0 )
sign = -1;
else
return
str;
}
else
if
( c ==
'+'
)
{
if
( i == 0 )
sign = 1;
else
return
str;
}
else
return
str;
}
if
( res < Integer.MAX_VALUE )
return
( int ) res * sign;
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这些方法。为了性能考虑,你应当手动解析它们。 |