使用Java8的Nashorn彌補Node.js密集計算的缺陷

banq發表於2014-03-26
NodeJS帶來的原生非同步併發與事件驅動程式設計模型得到認可,但是因為其單執行緒緣故,不能簡單方便地從事密集計算,而java優勢是多執行緒併發,Java 8又引入了Lambda表示式,使得Java多執行緒併發在處理高CPU負載的計算上既強大又方便,那麼我們是否對於Javascript中那些密集計算,比如對集合中的map操作,或聚合操作reduce使用Java實現呢?

答案是可以的,利用Java8的Javascript引擎 Nashorn。比如我們可以將Js如下的lambda在JVM中實現:

ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("nashorn");

String js;

js = "var map = Array.prototype.map \n";
js += "var names = [\"john\", \"jerry\", \"bob\"]\n";
js += "var a = map.call(names, function(name) { return name.length() })\n";
js += "print(a)";

engine.;
<p class="indent">


Nashorn是用來替代原來Java中的Javacript直譯器Rhino, 執行JS速度大大提升,能夠使用V8s環境,下面看看如何編譯lambda表示式。

不同於Java 和Scala 編譯器,是將檔案編譯成持久檔案,如.class檔案等,Nashorn是在記憶體中編譯,然後將位元組碼直接傳遞到JVM,Java 8使用invokerDynamic來連線Lambda函式程式碼。

invokerDynamic是在Java 7中加入允許程式設計師寫動態語言,然後決定在執行時如何連線這些程式碼。

對於Java和Scala語言,編譯器是在編譯時間決定哪個方法被呼叫,執行時是透過標準的ClassLoader來尋找相應的類,即使像方法過載解決也是編譯時間實現的。

對於動態語言,如js,靜態的解決辦法就不適用了,當我們在Java中說obj.foo()時,其實是obj有一個類的方法為foo(),而JS執行時是依賴被obj引用的實際物件,這對於靜態語言是一個夢魘,因為編譯和執行是分開的,編譯時連線到這個物件但是卻沒有執行。但是invokeDynamic可以做到。

invokeDynamic能夠推遲這種聯動到執行時間,這樣在執行時它能指導JVM呼叫哪個方法。靜態語言和動態語言在這點上是雙贏的。JVM得到要連線的實際方法,然後最佳化執行。

Nashorn也是這樣高效地實現連線,下面看個例子可以瞭解如何工作的,下面是返回JS陣列值:
invokedynamic 0 "dyn:getProp|getElem|getMethod:prototype":(Ljava/lang/Object;)Ljava/lang/Object;

Nashorn要求JVM在執行傳遞給它這個字串,作為交換,它會返回一個方法的一個處理器,用於接收物件和返回一個,只要JVM得到這個方法的一個處理器,它就能連線。

詳細流暢比較複雜,可見文章:Java 8: Compiling Lambda Expressions in The New Nashorn JS Engine


相關文章