異常高效使用小竅門 -- 讀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

相關文章
相關標籤/搜索