torutkのブログ

ソフトウェア・エンジニアのブログ

実行可能JARファイルをバッチファイルまたはシェルスクリプトに結合して実行する

Javaのプログラムを実行可能JAR形式で作成すると、プログラムを配布するのがJARファイル1つで済みます*1。実行可能JAR形式ファイルを実行するには、Windows OS上にOracle JREJava Runtime Environment)がインストールされていれば、ファイルをエクスプローラー上でダブルクリックしても、コマンドプロンプトからJARファイル名だけを指定して実行してもプログラムが立ち上がります。

LinuxCentOS 6)の場合は、Oracle JRERPMパッケージでインストールしていると、コマンド環境ではJARファイル名だけを指定して実行するとプログラムが立ち上がります。GNOMEデスクトップ上のファイルフォルダ(ノーチラス?)は残念ながら書庫ツールが開いてしまいました(JARはZIP形式なので)。なお、OpenJDKでは、コマンドラインでもファイル名だけ指定しただけでは実行されませんでした。

このように便利な(Linux上でのOpenJDKを除く)実行可能JARですが、次のデメリットがあります。

  • JavaVMオプションの指定が困難*2
  • 使用するJREのバージョンを指定することができない

JavaVMオプションの指定ができないと、使用するメモリ(ヒープサイズ)の指定、ガベージコレクターの指定、ロケールエンコーディング指定などが、デフォルト(お任せ)になってしまいます。
デフォルトの値で困ることとして、複数のJavaVMを同時に実行すると、メモリの使用、CPUの使用が過剰となってマシン全体に影響を与えることがあります。

物理メモリ8GBを搭載したPCで、64bit OS上で64bit版のJavaVMを実行すると、ヒープサイズが初期値で128MB*3、パラレルガベージコレクタが選択されるのでGCの際に搭載CPU(コア)をフル使用します。このJavaVMを多数実行すると、相当のメモリ使用量、CPU使用量となります。そのため、小さなプログラムを多数動かす際には、ヒープサイズを小さめに、ガベージコレクタもシリアル型にしたいというニーズがあります。

せっかく単一ファイルを配布して実行できる実行可能JAR形式ですが、JavaVMオプションなどを指定したいときは、別途バッチファイルやシェルスクリプトを用意して、そこからJavaVMオプションやJARファイルを指定して実行する形になってしまいます。

そんなときに、先週土曜日に開催したJava読書会の2次会で、JARファイルをシェルスクリプトに結合するのもできるよ、ということを聞きました。
ファイルの最初がテキストのシェルスクリプト、その後ろにJARファイルを結合したファイルとし、スクリプト部分には、java -jarに続けて自分自身のパスを指定、JARファイルはZIP書庫形式で、これはファイル先頭にゴミがあっても無視するので実行可能ということだそうです。

シェルスクリプトに結合する場合について分かりやすく解説しているブログがありました。
実行可能 jar をコマンドっぽく実行するために(java -jar 使いたくない) - Qiita

これを参考に、シェルスクリプトLinux)とバッチファイル(Windows)で試してみました。

まず、Hello World的なJavaのコードを書いて実行可能JARファイルを作成します。実行可能JARファイルの生成は主要なIDEなら標準で対応しています。
次に、このJARファイルを呼び出すシェルスクリプト、バッチファイルを記述します。

シェルスクリプト

hello.src.sh ファイルを作成し、以下を記述します。

#!/bin/sh

JAVA_OPT="-Xms8m -Xmx64m -Xss256k -XX:+UseSerialGC"

java $JAVA_OPT -jar "$0" "$@"

exit $?
JARファイルを結合したシェルスクリプトの作成と実行
~$ cat hello.src.sh hello.jar > hello.sh
~$ chmod +x hello.sh
~$ ./hello.sh

バッチファイル

hello.src.bat ファイルを作成し、以下を記述します。

@echo off

set JAVA_OPT=-Xms8m -Xmx64m -Xss256k -XX:+UseSerialGC

java %JAVA_OPT% -jar "%~f0" %*

exit /b %ERRORLEVEL%
JARファイルを結合したバッチファイルの作成と実行
C:\work> copy /b hello.src.bat+hello.jar hello.bat
C:\work> hello

*1:Java標準以外の依存ライブラリを利用していると、複数のファイルになってしまいます

*2:環境変数JAVA_TOOL_OPTIONSを定義していれば、これに定義した内容がJavaVMオプションとして渡されますが、他のjavaコマンドにも適用されてしまいます

*3:搭載物理メモリの64分の1のサイズ