Java8で帳票クリエータを実行すると遅いのはNashornが遅いから

こんにちは、開発担当の Masa です。

これまで何回か Nashorn 関連の話題を出しましたが、「シーオーリポーツ 帳票クリエータ Ver.3 for Java」は Lot_012 で無事 Java8 に対応することができました・・・が、Java7 と比べて実行速度が遅いことが発覚しました。
Java8 自体は Java7 よりも速いはずなのに何故!?というわけで、調べてみると原因は Nashorn でした。

まずは動かぬ証拠を。
簡単なスクリプトを追加した帳票を3ページ出力する処理を、Java7、Java8、Java8 + Rhino で実行しました。

 

Java8(Nashorn)は遅いですね。比べて Java8 + Rhino は速いです。

※Java8 + Rhino の環境構築については前々回を参照

Rhino Nashorn
Java7 1,721ms -
Java8 1,325ms 2,909ms

 

Nashorn は"関数定義が異常に遅いが実行速度は高速"らしいのですが、組み込み関数を入れても 10 回に満たない関数定義でそこまで差が出るとは思えず。
Java VisualVM で調べてみました。

3ページだと差が分かりにくいので、"スクリプト内での関数定義無し"で 100ページ出力するプログラムをサンプリング。

 

Java7(Rhino)はこう。
※JOPtion~は関係ないので無視してください

呼び出しツリー_Rhino

帳票クリエータの処理だけを合計すると、1,299ms

ホット・スポットを見ると、eval299ms かかってますね。許容できる範囲ですけども。

ホット・スポット_Rhino

 

さて、お待ちかねの Java8(Nashorn)!!
※JOPtion~は関係ないので無視してください

呼び出しツリー1

帳票クリエータの処理だけを合計すると、衝撃の 14,299ms 。桁が違います。

ホット・スポットを見ると、java.lang.Class.forName() だけで 12,495ms。嫌な予感しかしません。

ホット・スポット

詳細を見ると、グローバルスコープにオブジェクトを設定している箇所で 10,205ms
というか、eval2,392ms ですけど(ScriptManager.eval の中は nashorn の eval です)。

呼び出しツリー3

もっと詳細をドン!
居ましたね。java.lang.Class.forName()

呼び出しツリー2

しかもこれ、remove です。もちろん put でも同様に java.lang.Class.forName()

グローバルスコープにオブジェクトを追加/削除した時点で該当のクラスをロードしてるように見えます。
何でしょう、コレ。

全部使うとは限らないし、せめて最初に使用する時にしてくれないでしょうか。。。あと remove でロードはやめて欲しい(切実)。
当分 Java8 + Rhino で実行するのが良さそうです。