异常高效使用小窍门 -- 读Scala源码有感

另载于 http://www.qingjingjie.com/blogs/11

熟悉Scala的人知道返回值是代码块的最后一句,一般不能提前返回。return关键字是用抛异常来实现的,这样就能提前脱离代码块了。

最近看Scala源代码,注意到它对return的高效实现,有趣。

Scala咋实现的?

抛的异常类是NonLocalReturnControl,继承自Throwable,有个字段用来放置要返回的值。编译器把return value大致翻译成throw new NonLocalReturnControl(key/*metadata*/, value)就可以了。

通常Java的异常有三类: Exception, RuntimeException, Error。如果一个对象是Exception但不是RuntimeException,那就是checked exception。而Error通常是JVM抛出的,例如StackOverflowError, OutOfMemoryError, VerifyError,偶尔也有XML库抛Error。总之没有其他直接继承Throwable的类了。

所以在Scala里只要注意别无脑catch Throwable,就不会误拦return了。如果是个服务器程序,不想let it crash,只要catch Exception和Error就好了。

用抛异常来打断控制流的做法,在有的Web框架中也出现了,没啥。但在一门语言的实现中,必须保证高效。

于是我们要知道抛异常为什么慢!Scala咋解决的?

throw和catch都是很快很快的,毕竟只是几个地址操作,慢的是new Exception这一步,这里要让JVM取得当前的一大串stacktrace填充进去,开销约为new 200个Object的程度。

Scala的NonLocalReturnControl的窍门是——Override了fillInStackTrace()方法。这个方法本来会调用native的fillInStackTrace(int)让JVM去填stacktrace。覆盖成空实现,测一下,开销约为new 6个Object。效果拔群!

还可以提高!去掉synchronized关键字,开销又减半了。最后是new 3个Object的开销,可以接受!

我想到另一种实现方案是把value塞到一个ThreadLocal里,异常就不必包含value了,这样就只需全局new一次异常对象,每次都throw它。开销约为new 1个Object。Clojure就喜欢用ThreadLocal来实现一些特性。不过ThreadLocal悠着点用啊,玩不好是要出大事的(下一篇谈这个问题)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值