Java异常的性能分析
详见: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 secMade 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 secPack: Made 1.000.000 iterations for string '12345a' : time = 23.248 secstrToObject: Made 100.000.000 iterations for string '12345' : time = 6.311 secstrToObject: Made 100.000.000 iterations for string '12345a' : time = 5.807 sec |
总结
千万不要把异常当成返回码一样用,或者当作可能发生的事件(尤其是和IO无关的方法)往外抛。抛异常的代价太昂贵了,对于一般的方法,至少要慢百倍以上。
如果你每条数据都需要解析,又经常会出现非数值串的时候,尽量不要用Number子类型的parse*/valueOf这些方法。为了性能考虑,你应当手动解析它们。
Java异常的性能分析的更多相关文章
- Java应用常用性能分析工具
Java应用常用性能分析工具 好的工具有能有效改善和提高工作效率或加速分析问题的进度,笔者将从事Java工作中常用的性能工具和大家分享下,如果感觉有用记得投一票哦,如果你有好的工具也可以分享给我 工具 ...
- JAVA 异常对于性能的影响
陶炳哲 - MAY 12, 2015 在对OneAPM的客户做技术支持时,我们常常会看到很多客户根本没意识到的异常.在消除了这些异常之后,代码运行速度与以前相比大幅提升.这让我们产生一种猜测,就是在代 ...
- Java几款性能分析工具的对比
在给客户的应用程序维护的过程中,我注意到在高负载下的一些性能问题.理论上,增加对应用程序的负载会使性能等比率的下降.然而,我认为性能下降的比率远远高于负载的增加.我也发现,性能可以通过改变应用程序的逻 ...
- [转]Java Thread Dump 性能分析
Java and Thread 一个 web 服务器使用几十到几百个线程来处理大量并发用户,如果一个或多个线程使用相同的资源,线程之间的竞争就不可避免了,并且有时候可能会发生死锁. Thread co ...
- 使用JDK自带的VisualVM进行Java程序的性能分析
VisualVM是什么? VisualVM是JDK自带的一个用于Java程序性能分析的工具,JDK安装完毕后就有啦,在JDK安装目录的bin文件夹下能找到名称为jvisualvm.exe. 要使用Vi ...
- Java 性能分析工具 , 第 1 部分: 操作系统工具
引言 性能分析的前提是将应用程序内部的运行状况以及应用运行环境的状况以一种可视化的方式更加直接的展现出来,如何来达到这种可视化的展示呢?我们需要配合使用操作系统中集成的程序监控工具和 Java 中内置 ...
- Java 性能分析工具 , 第 3 部分: Java Mission Control
引言 本文为 Java 性能分析工具系列文章第三篇,这里将介绍如何使用 Java 任务控制器 Java Mission Control 深入分析 Java 应用程序的性能,为程序开发人员在使用 Jav ...
- 软件性能测试分析与调优实践之路-Java应用程序的性能分析与调优-手稿节选
Java编程语言自从诞生起,就成为了一门非常流行的编程语言,覆盖了互联网.安卓应用.后端应用.大数据等很多技术领域,因此Java应用程序的性能分析和调优也是一门非常重要的课题.Java应用程序的性能直 ...
- Java 性能分析工具 , 第 2 部分:Java 内置监控工具
引言 本文为 Java 性能分析工具系列文章第二篇,第一篇:操作系统工具.在本文中将介绍如何使用 Java 内置监控工具更加深入的了解 Java 应用程序和 JVM 本身.在 JDK 中有许多内置的工 ...
随机推荐
- npm的package.json字段含义中文文档
简介 本文档有所有package.json中必要的配置.它必须是真正的json,而不是js对象. 本文档中描述的很多行为都受npm-config(7)的影响. 默认值 npm会根据包内容设置一些默认值 ...
- vue错误和解决方法
1.Error in render function: "TypeError: Cannot read property 'matched' of undefined 原因:之前不知道,在引 ...
- 原理Lambda表达式
http://www.kingreatwill.com/ (1)源起 .net的设计者发现在使用匿名方法时, 仍旧有一些多余的字母或单词的编码工作 比如delegate关键字 于是进一步简化了匿名方法 ...
- android.intent.action.MAIN与android.intent.category.LAUNCHER
android.intent.action.MAIN 决定应用程序最先启动的Activity android.intent.category.LAUNCHER 决定应用程序是否显示在程序列表里 在网上 ...
- 扩展Python模块系列(二)----一个简单的例子
本节使用一个简单的例子引出Python C/C++ API的详细使用方法.针对的是CPython的解释器. 目标:创建一个Python内建模块test,提供一个功能函数distance, 计算空间中两 ...
- 怎样做才是最优雅方式切换 web 项目数据源 ?
随着业务变迁/需求变更,JavaEE 应用中会被迫连接多个数据源进行业务处理. 怎样在不影响原有项目结构的情况下,已最优雅/最简洁的方式动态切换数据源呢? 本文已一次添加数据源后动态切换实践为例,描述 ...
- 甲方VS乙方
甲方与乙方,在很多人都存在有误解,不知道究竟如何辨别.这里我提一些简单的辨别方式:甲方一般是指提出目标的一方,在合同拟订过程中主要是提出要实现什么目标,乙方一般是指完成目标,在合同中主要是提出如何保证 ...
- 运行和控制Nginx
Nginx命令行选项 启动.停止.重启nginx命令 如何重新加载nginx配置文件 nginx程序不重启平滑升级可执行文件 nginx命令行参数 不像许多其他软件系统,Nginx 仅有几个命令行 ...
- PostgreSQL存储过程(3)-流程控制语句
流程控制 讲解条件语句,循环语句. 本次环境会用到表emp:下面是emp执行语句. drop table emp; create table EMP( EMPNO numeric() not null ...
- String、StringBuffer与StringBuilder
String.StringBuffer与StringBuilder之间区别 最近学习到StringBuffer,心中有好些疑问,搜索了一些关于String,StringBuffer,StringBui ...