Java7 と Java8 ランタイム配備の違い、または如何にして CurrentVersion has value '1.8', but '1.7' is required エラーが起こるか

f:id:Naotsugu:20180227121532p:plain


はじめに

Windows 環境での話し。

いまさらの話し。

Java7 と Java8 でライタイムの配備方法が変更され、以下のエラーで起動できないことがある。

Error: Registry key 'Software\JavaSoft\Java Runtime Environment'\CurrentVersion'
has value '1.8', but '1.7' is required.

特にサービスから起動する Javaアプリケーションは注意が必要。


JDK7(あるいはそれ以前の)インストール時の挙動

JDK 7のインストール時にパブリックJREWindows\System32 にコピーされる。具体的には java.exe, javaw.exe, javaws.exe がコピーされる。

32 bit 版をインストールした場合は Windows\SysWOW64 にコピーされる。WOW64 は Windows 32-bit On Windows 64-bit で、32 bit 版のバイナリが入っており実行時には 64 bit にエミュレートされる。System32 側は名前に反して64 bit バイナリが入ることになる。

Windows\System32java.exe が入るので、環境変数として別途 PATH を通すことなく Java が利用できるようになる。という開発者視点から見るとおせっかいな方法が取られている。


JDK8(あるいはそれ以降の)インストール時の挙動

JDK 8 のインストール時には「システム環境変数」(ユーザー環境変数ではない) PATH の先頭にC:\ProgramData\Oracle\Java\javapath; が追加される。そしてこの位置に java.exe, javaw.exe, javaws.exe がコピーされる。

PATH の定義中に C:\ProgramData\Oracle\Java\javapath; が存在しなければ先頭に追加し、存在すれば追加しないという挙動になる。

通常は C:\Windows\System32; よりも C:\ProgramData\Oracle\Java\javapath; が先頭にくるため、 java.exeC:\ProgramData\Oracle\Java\javapath; にある Java8 のものが優先的に利用されることになる。


Java Runtime Environment \ CurrentVersion

システム中の Java の現在のバージョンは以下のキーのレジストリ値として設定されている。

HKEY_LOCAL_MACHINE\Software\JavaSoft\Java Runtime Environment'\CurrentVersion

この値が 1.7 とか 1.8 とか設定される。


そして、先に見た C:\Windows\System32;C:\ProgramData\Oracle\Java\javapath; にコピーされた java.exe は起動時にレジストリ中の現在バージョンとのチェックを行う。


レジストリの現在バージョンに 1.8 が設定されていた場合、Windows\System32\java.exe つまり1.7の java.exe を実行すると以下のようなエラーになり実行できない。

Error: Registry key 'Software\JavaSoft\Java Runtime Environment'\CurrentVersion'
has value '1.8', but '1.7' is required.


逆にレジストリ1.7 が設定されており、C:\ProgramData\Oracle\Java\javapath 側の 1.8 の java.exe を実行した場合も同様にエラーになり実行できない。

Error: Registry key 'Software\JavaSoft\Java Runtime Environment'\CurrentVersion'
has value '1.7', but '1.8' is required.


CurrentVersion とのチェックを行うのは、C:\Windows\System32C:\ProgramData\Oracle\Java\javapath にコピーされた java.exe だけで、C:\Program Files\Java\jdk1.X.X\bin\java.exe を直接指定した場合にはチェックは行われずエラーにはならない。


Windows サービス起動する Java アプリの場合話しがややこしくなる

Java7 と Java8 が共存するケースで Windows のサービスからパス指定なしの java.exe コマンドを起動した場合は以下のエラーに直目することになる。

Error: Registry key 'Software\JavaSoft\Java Runtime Environment'\CurrentVersion'
has value '1.8', but '1.7' is required.


サービス起動した場合のアカウントは(アカウントを特に指定しない場合)SYSTEMアカウントとなり、システム環境変数が読み込まれる(ユーザ環境変数ではない)。

ちなみにシステム環境変数を変更した後は、再起動しないとサービス起動時のシステム環境変数に反映されない。Services.exe の環境変数はシステム起動時に読み込まれ、サービスの起動は Services.exe から環境変数を引き継ぐためとなる。

システム環境変数PATH には、つまりC:\ProgramData\Oracle\Java\javapath が先頭にあれば 1.8 が使われる。

しかし、サービス起動時のカレントディレクトリはC:\Windows\System32 であり、ここに直接コピーされた java.exe が優先的に使われてしまう。

これにより 1.7 の java.exe が起動し、レジストリCurrentVersion に設定された 1.8 との違いによりエラーとなり起動できなくなってしまう。


サービス起動時の java を明示的にパス指定しても良いが、C:\Windows\System32 にコピーされた java.exe, javaw.exe, javaws.exe を削除して、切り替えたい場合はJAVA_HOME など PATH の設定でなんとかする方が良い。

そもそもC:\Windows\System32 に直接コピーなんて気持ち悪いので早い目に消し去ってあげたい。


Java7 と Java8 では MaxTenuringThreshold の上限も違う

ついでの話し。

Java8 になって Permanent領域 が無くなり Nativeメモリ側に Metaspace として定義されるようになったので、Permanent領域の VM オプションは非推奨でワーニングになる。

Java7 Java8
-XX:PermSize -XX:MetaspaceSize
-XX:MaxPermSize -XX:MaxMetaspaceSize

のは有名な話し。


Java8では -XX:MaxTenuringThreshold の値が 0 ~15 では無い場合にエラーとなり起動出来なくなった。

Java5 だか Java6 だかの時代には 32 がデフォルトだったが、この値を明示的に指定していた場合には Java8 にすると JVM が起動できなくなる。

パラレル(スループット)コレクタの場合 15 で、CMSコレクタの場合 6 がデフォルト値。あえて指定する必要もないかな。



Java 9 Modularity: Patterns and Practices for Developing Maintainable Applications (English Edition)

Java 9 Modularity: Patterns and Practices for Developing Maintainable Applications (English Edition)