プログラマ38の日記

主にプログラムメモです。

Java: JDK9 jshellを使ってみたメモ

jdk9でjshellが追加されました。

簡単なバッチプログラムをjavaで書くことが多いのですが、コンパイルしたclassファイルよりも、テキストのjavaコードをそのまま実行するほうが好都合な時があります。

 

例えば、後で変更が見えてるような場合、柔軟に変更に対応できる設定ファイルの仕様を考えるより、プログラムファイル自体をテキストで用意する方が簡単です。

 

テキストのjavaコードを実行する仕組みとして、BeanShellを使っていましたが、使い勝手が良ければ今後はjshellをメインに使っていこうかと思っています。

 

以下、jshellをバッチ処理用途として使ってみてわかったことのメモになります。

 

コマンド形式

jshell -s --execution local  <スクリプトファイル>

スクリプトファイルを実行する場合、最小限のコマンドは上記となります。
jshell -h とうつことで、コマンドの引数が一覧でわかります。

 

最小限として -s と --execution local は必須としました。

-sを指定することでフィードバックを最小限としています。

そして、--execution local を指定することで、スクリプトの戻り値を取得できるようにしています。--execution local を指定しない場合、スクリプト内でSystem.exit(1); としてもjshellを完了できません。(これ以降 --execution localの前提での記載となります)

さらにclasspathを追加する場合は、--class-path を使います。

 

スクリプトへ引数を渡す

jshellコマンドには、スクリプトファイルへ引数を渡す仕組みがありません。

システム変数か、環境変数で渡すことになります。

rem 1.環境変数の場合
set envvar1=test1

jshell -s --execution local

-> System.out.println( System.getenv("envvar1"));
test1
rem 2.システム変数の場合
jshell -s --execution local  -J-Dpropvar1=var2
-> System.out.println( System.getProperty("propvar1"));
var2

 

スクリプトファイルの文字コード

jshellの引数のスクリプトファイルは、Windowsであれば、Windows-31Jとなりますが、どうしてもutf8で実行したい場合は、システム変数で指定します。

jshell -s --execution local  -J-Dfile.encoding=utf8 <スクリプトファイル>

ただし、utf8のスクリプトファイルがBOM有だとエラーが出ます。

 

vm引数を指定する

メモリサイズ(-Xms -Xmx)などや -server オプションなどは、-J のオプションで指定します。既に上記でシステム変数として使っていますけど。。

jshell -s --execution local  -J-server -J-Xmx3G

--execution local を指定しているので -J のオプションですが、--execution local を指定しない場合、-R のオプションで指定します。

※--execution localを指定しないと、jshellの中でさらに別のvmを起動するようで、そのvmへのオプションは -R として指定するようです。

 

スクリプトの戻り値を取得する

--execution local を指定してjshellを起動し、System.exitで終了します。

jshell -s --execution local
-> System.exit(5);

echo %ERRORLEVEL%
5

 

スクリプト構文

BeanShellと大体同じだなーと思いました。多少違いがあって、スクリプト内の関数はstaticでは定義できなかったりします。BeanShellでは、ジェネリクスや可変引数が使用できませんでしたが、jshellでは使えるのが嬉しいです。

スクリプト内の関数は、呼び出す前に定義しておくことが必要、java.utilなどは最初からimportされているのはBeanShellと同じです。

//サンプルスクリプト
public void func1(){
  System.out.println("func1 called");
}

func1();

System.out.println(Arrays.asList("test1","test2","test3"));
System.exit(4);

 

スクリプト実行の性能

気になるのが、実行性能だと思います。jdk9でjrunscript , jshell BeanShell , コンパイル済みコードで比較してみたいと思います。

比較で使用したのは次のコードです。(フィボナッチ数列です。引数に40を指定しました)

  public int fibonacci(int n) {
       return ( n==1 || n==0 ) ? n:fibonacci(n-2) + fibonacci(n-1);
  }

 

結果は次の表の通りとなり、jshellは、コンパイル済みjavaとほぼ同じとなっています。(バイトコードを作成する仕組みなのかなと思いますが、詳しくことはわかりません)

BeanShellは、比較するととても遅いですが、性能を気にしなくていい時だけ使うので実用上はそこまで問題にはなりません。どちらかといえばnashornの性能の良さに目を見張りました。rhinoよりかなり良くなっていると思います。

実行プログラム 所要時間
(ミリ秒)
実行コード
java
コンパイル済み
661  public void sample1() {
  long start = System.currentTimeMillis(); 
  int ret =fibonacci(40);
  long end = System.currentTimeMillis();
  System.out.println( end - start );
 }
 
 public int fibonacci(int n) {
     return ( n==1 || n==0 ) ? n:fibonacci(n-2) + fibonacci(n-1);
 }

 public static void main(String[] args) {
  new Sample().sample1();
 }
jshell 672 public void sample1() {
  long start = System.currentTimeMillis(); 
  int ret =fibonacci(40);
  long end = System.currentTimeMillis();
  System.out.println( end - start );
}
 
public int fibonacci(int n) {
     return ( n==1 || n==0 ) ? n:fibonacci(n-2) + fibonacci(n-1);
}

sample1();

jrunscript

[nashorn]

1488 var lib = new JavaImporter(java.lang);
with(lib){
  function sample1() {
    var start = System.currentTimeMillis(); 
    var ret =fibonacci(40);
    var end = System.currentTimeMillis();
    System.out.println( end - start );
  } 
  function fibonacci(n) {
     return ( n==1 || n==0 ) ? n:fibonacci(n-2) + fibonacci(n-1);
  }

  sample1();
}
BeanShell 389118 public void sample1() {
    long start = System.currentTimeMillis();
    int ret =fibonacci(40);
    System.out.println(ret);
    long end = System.currentTimeMillis();
    System.out.println( (end - start) );
}
public int fibonacci(int n) {
   return ( n==1 || n==0 ) ? n:fibonacci(n-2) + fibonacci(n-1);
}

sample1();

※性能は、同じPCで同じJDK9で実施しました。

最後に

BeanShellだとジェネリクスや可変長引数が使えなかったりして構文エラーにつまづくことがあります。

最新のJava構文が利用できて、性能面も問題ないのでjdk9が普及してきたらjshellは積極的に使っていきたいなと思いました。でも当面はjdk8を使うと思うので、BeanShellを使っていきます。