老哥哔哔叨
大家应该都经历过双十一
吧,那个流量大的恐怖
吧,那个并发高的吓人
吧。那么在一个高并发的系统里,有哪些点是影响系统性能的呢,今天我们来讲其中一个点:自定义异常
如果对大家有所帮助,请给个【在看】和【点赞】
疯狂的异常
为什么异常会影响性能
首先给大家看一段JDK
的Throwable
源码
public synchronized Throwable fillInStackTrace() {
if (stackTrace != null ||
backtrace != null /* Out of protocol state */ ) {
fillInStackTrace(0);
stackTrace = UNASSIGNED_STACK;
}
return this;
}
上面这段JDK
的源码就是抛出异常时会调用的方法,这段方法暴露出两个问题
使用了
synchronized
修饰整个异常方法将异常追踪信息放到了堆栈中(想想
JVM
和线程
)
异常种类
业务异常
这些是我们自定义的、可以预知的异常,抛出这种异常并不表示系统出了问题,而是正常业务逻辑上的需要,例如用户名密码错误、参数错误等。
系统异常
往往是运行时异常,比如数据库连接失败、IO 失败、空指针等,这种异常的产生多数表示系统存在问题,需要人工排查定位。
相信大家都接触过异常,对于业务异常,我们只需要简单的知道一个描述问题的字符串即可,栈追踪信息对我们的意义并不大。而对于系统异常,追踪信息才是排查错误不可或缺的参考。
大家试想,如果前端传的参数错了,系统里就抛出一个异常,那么在双十一的情况下一秒钟得抛出多少个异常呢?
问题思考
抛异常的时候是不是会被 synchronized 上同步锁?
需不需要线程去执行?
是不是得创建异常对象?
需不需要堆栈去存储?
需不需要 jvm 去垃圾回收?
性能测试
创建普通 Java 对象 (CustomObject extends HashMap)
创建普通 Java 异常对象(CustomException extends Exception)
创建改进的 Java 业务异常对象 (CustomException extends Exception,覆写 fillInStackTrace 方法,并且去掉同步)
测试结果
(运行环境:xen 虚拟机,5.5G 内存,8 核;jdk1.6.0_18)
(10 个线程,创建 10000000 个对象所需时间)
普通 Java 对象:
45284 MS
普通 java 异常:
205482 MS
改进的 Java 业务异常:
16731 MS
大家可以看到正常抛出 Exception 的和覆写了 fillInStackTrace 的 Exception,性能差距了很多倍,如果高并发的系统里,就像雪球一样越滚越大。影响系统的并发量。
解决方案:覆写 fillInStackTrace
我们来看看非常 NB 的kafka源码
是如何优化的。
/* avoid the expensive and useless stack trace for api exceptions */
/* 翻译:避免对api异常进行昂贵且无用的堆栈跟踪 */
@Override
public Throwable fillInStackTrace() {
return this;
}
很多开源框架都是这样处理,避免不必要的性能浪费。
老哥结语
什么是匠人精神,就是将一件事情做到极致。优化永无止境,且行且珍惜。
IT 老哥
一个在大厂做高级Java开发的程序猿
关注微信公众号:IT 老哥
回复:Java 全套教程,即可领取:Java 基础、Java web、JavaEE 全部的教程,包括 spring boot 等
回复:简历模板,即可获取 100 份精美简历
回复:Java 学习路线,即可获取最新最全的一份学习路线图
回复:Java 电子书,即可领取 13 本顶级程序员必读书籍