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

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

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

Scala咋实现的?

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

一般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就行了。code

用抛异常来打断控制流的作法,在有的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的开销,能够接受!it

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

相关文章
相关标签/搜索