JavaのJDK/JRE/Server JREの使い分け

先日、OracleのWebページからJDKJREをダウンロードしようとしたら、「Server JRE」というのが用意されていることに気がついた。

このServer JREとは、いったい何じゃらほい? というのが今回のテーマ。なお基本的にLinuxサーバ環境で考えます(Windows, Solarisは無視)。

JDKJRE

はじめに、複雑怪奇なJavaパッケージについて少し解説しておく。「Javaをインストール」という場合には、開発キットであるJDKとランタイムであるJRE、どちらをインストールするかが問題になる。しかしこの日記を読んでいるような人ならばJava開発者であろうから、JDKを入れてしまえばほぼ間違いなく問題は解決する。

なぜなら、JDKJREを内包しているので、JDKをインストールすれば自動的にJREも入るからである。というわけで開発者なら、何も考えずにJDKを入れてしまえば良い(動作検証のため複数バージョンのJRE混在とか、いろいろ問題は山積みだが、とりあえずその辺は置いておく)。

Server JRE

2013年4月にリリースされたJava SE 7u21から、JDKJREに加えて、Server JREという3つめのJavaが配布されるようになった(http://www.jpcert.or.jp/tips/2013/wr132201.html)。

結論から言うと、このServer JREとは、サーバ向けのJREである。……とだけ書くとさっぱり説明になっていない気がするので、もう少し背景を説明すると。

昨今、何かとJava脆弱性が話題になることが多い(最近の例だと、Java 7u10の脆弱性)。そのためTomcat環境などでJavaアプリケーションを動かしているLinuxサーバ管理者も、日々サーバにインストールしているJavaのアップデートをおこなわなければいけない。

しかしJava脆弱性のほとんどは、JavaアプレットなどWebブラウザプラグインで動作する部分の脆弱性である。サーバサイドJavaでは当然このような脆弱性は全く関係ないのだが、何しろJavaのパッケージはJREならJRE、一つだけしかないので、政治的な理由や社内ルールなどで「脆弱性が出ているパッケージを使い続けるのか!?」と言われて渋々アップデート対応している人も多いだろう。

この問題は、例えばLinuxならば「デスクトップLinux向けのJava」と、「サーバLinux向けのJava」が分けて提供されていれば解決することになる。サーバLinux向けのJavaには、脆弱性がしょっちゅう見つかるWebブラウザ向けのプラグインなどが入っていなければ嬉しいわけだ。今回のServer JREの提供は、まさにこの用途である。

Server JREのファイル群

Server JREのパッケージ(server-jre-7u51-linux-x64.tar.gz)を展開してみると、JREと言いつつ[jdk1.7.0_51]というディレクトリ名で展開される。このディレクトリ名からも分かるとおり、基本的にServer JREのベースはJREではなく、JDKと考えた方が良い。

ただしServer JREは、Webブラウザ向けプラグイン絡みのライブラリがごっそり削除されている。例えばJDKならば存在したjre/lib/plugin.jarファイルなどが同梱されていない。これで余計なセキュリティリスクを無くしているわけだ。

なお基本的なファイルは揃っているため、今まで動いていたアプリはだいたい動くはずである。Server JREでTomat 7.0.52を動作させてみたところ、私の試した範囲では何も問題は無かった。

JVM監視ツール

Server JREは、通常のJREでは同梱されていないjpsコマンドやjstatコマンドが、binディレクトリに入っている。以下にJREとServer JREでのbinディレクトリのファイルリストの差を書いておく。

[ozuma@cent jre1.7.0_51]$ ls -laF bin
合計 452
drwxr-xr-x. 2 uucp 143   4096 12月 19 06:39 2013 ./
drwxr-xr-x. 6 uucp 143   4096 12月 19 12:15 2013 ../
lrwxrwxrwx. 1 uucp 143      8  3月  1 00:37 2014 ControlPanel -> jcontrol*
-rwxr-xr-x. 1 uucp 143   7718 12月 19 12:13 2013 java*
-rwxr-xr-x. 1 uucp 143  19320 12月 19 12:14 2013 java_vm*
-rwxr-xr-x. 1 uucp 143 128728 12月 19 12:14 2013 javaws*
-rwxr-xr-x. 1 uucp 143   6391 12月 19 12:14 2013 jcontrol*
-rwxr-xr-x. 1 uucp 143   7925 12月 19 12:13 2013 keytool*
-rwxr-xr-x. 1 uucp 143   8117 12月 19 12:13 2013 orbd*
-rwxr-xr-x. 1 uucp 143   7957 12月 19 12:13 2013 pack200*
-rwxr-xr-x. 1 uucp 143   7981 12月 19 12:13 2013 policytool*
-rwxr-xr-x. 1 uucp 143   7925 12月 19 12:13 2013 rmid*
-rwxr-xr-x. 1 uucp 143   7933 12月 19 12:13 2013 rmiregistry*
-rwxr-xr-x. 1 uucp 143   7949 12月 19 12:13 2013 servertool*
-rwxr-xr-x. 1 uucp 143   8149 12月 19 12:13 2013 tnameserv*
-rwxr-xr-x. 1 uucp 143 220104 12月 19 12:13 2013 unpack200*
  • Server JREの場合

(分かりやすくするためディレクトリ名を[server-jre-7u51]にリネームしている)

[ozuma@cent server-jre-7u51]$ ls -laF bin
合計 544
drwxr-xr-x. 2 uucp 143   4096 12月 19 12:19 2013 ./
drwxr-xr-x. 8 uucp 143   4096 12月 19 12:15 2013 ../
-rwxr-xr-x. 1 uucp 143   7949 12月 19 12:13 2013 appletviewer*
-rwxr-xr-x. 1 uucp 143   7925 12月 19 12:13 2013 apt*
-rwxr-xr-x. 1 uucp 143   7925 12月 19 12:13 2013 extcheck*
-rwxr-xr-x. 1 uucp 143   7957 12月 19 12:13 2013 idlj*
-rwxr-xr-x. 1 uucp 143   7909 12月 19 12:13 2013 jar*
-rwxr-xr-x. 1 uucp 143   7925 12月 19 12:13 2013 jarsigner*
-rwxr-xr-x. 1 uucp 143   7718 12月 19 12:13 2013 java*
-rwxr-xr-x. 1 uucp 143   1809 12月 19 12:13 2013 java-rmi.cgi*
-rwxr-xr-x. 1 uucp 143   7925 12月 19 12:13 2013 javac*
-rwxr-xr-x. 1 uucp 143   7925 12月 19 12:13 2013 javadoc*
-rwxr-xr-x. 1 uucp 143   2052 12月 19 06:39 2013 javafxpackager*
-rwxr-xr-x. 1 uucp 143   7925 12月 19 12:13 2013 javah*
-rwxr-xr-x. 1 uucp 143   7925 12月 19 12:13 2013 javap*
-rwxr-xr-x. 1 uucp 143   7909 12月 19 12:13 2013 jcmd*
-rwxr-xr-x. 1 uucp 143   7997 12月 19 12:13 2013 jconsole*
-rwxr-xr-x. 1 uucp 143   7965 12月 19 12:13 2013 jdb*
-rwxr-xr-x. 1 uucp 143   7925 12月 19 12:13 2013 jhat*
-rwxr-xr-x. 1 uucp 143   8077 12月 19 12:13 2013 jinfo*
-rwxr-xr-x. 1 uucp 143   8077 12月 19 12:13 2013 jmap*
-rwxr-xr-x. 1 uucp 143   7909 12月 19 12:13 2013 jps*
-rwxr-xr-x. 1 uucp 143   7933 12月 19 12:13 2013 jrunscript*
-rwxr-xr-x. 1 uucp 143   7965 12月 19 12:13 2013 jsadebugd*
-rwxr-xr-x. 1 uucp 143   8077 12月 19 12:13 2013 jstack*
-rwxr-xr-x. 1 uucp 143   7909 12月 19 12:13 2013 jstat*
-rwxr-xr-x. 1 uucp 143   7925 12月 19 12:13 2013 jstatd*
-rwxr-xr-x. 1 uucp 143   5362  1月 17 15:12 2013 jvisualvm*
-rwxr-xr-x. 1 uucp 143   7925 12月 19 12:13 2013 keytool*
-rwxr-xr-x. 1 uucp 143   7933 12月 19 12:13 2013 native2ascii*
-rwxr-xr-x. 1 uucp 143   8117 12月 19 12:13 2013 orbd*
-rwxr-xr-x. 1 uucp 143   7957 12月 19 12:13 2013 pack200*
-rwxr-xr-x. 1 uucp 143   7981 12月 19 12:13 2013 policytool*
-rwxr-xr-x. 1 uucp 143   7909 12月 19 12:13 2013 rmic*
-rwxr-xr-x. 1 uucp 143   7925 12月 19 12:13 2013 rmid*
-rwxr-xr-x. 1 uucp 143   7933 12月 19 12:13 2013 rmiregistry*
-rwxr-xr-x. 1 uucp 143   7941 12月 19 12:13 2013 schemagen*
-rwxr-xr-x. 1 uucp 143   7925 12月 19 12:13 2013 serialver*
-rwxr-xr-x. 1 uucp 143   7949 12月 19 12:13 2013 servertool*
-rwxr-xr-x. 1 uucp 143   8149 12月 19 12:13 2013 tnameserv*
-rwxr-xr-x. 1 uucp 143 220104 12月 19 12:13 2013 unpack200*
-rwxr-xr-x. 1 uucp 143   7925 12月 19 12:13 2013 wsgen*
-rwxr-xr-x. 1 uucp 143   7941 12月 19 12:13 2013 wsimport*
-rwxr-xr-x. 1 uucp 143   7941 12月 19 12:13 2013 xjc*

見て分かる通り、Server JREのbinディレクトリはJDKで揃っているものがだいたい入っている。

例えば、JREだけでもTomcatを動かすことはできるのだけど、JREではjstatコマンドなどが用意されていないため、Tomcatのヒープメモリの状態を確認できずに困った……という経験をしたことのある人は多いだろう(む、私だけか?)。Server JREならば、これらのコマンドが入っているためLinuxサーバ上で安心して利用できる。

ちなみにjpsコマンドとjstatコマンドは以下のような感じで使う。おさらい。

$ jps -v
$ jstat -gcutil <PID> <interval time(ms)>

以下がTomcatのヒープメモリを確認する例。

$ /usr/local/server-jre-7u51/bin/jps -v
2039 Bootstrap -Djava.util.logging.config.file=/usr/local/apache-tomcat-7.0.52/conf/logging.properties -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager -Djava.endorsed.dirs=/usr/local/apache-tomcat-7.0.52/endorsed -Dcatalina.base=/usr/local/apache-tomcat-7.0.52 -Dcatalina.home=/usr/local/apache-tomcat-7.0.52 -Djava.io.tmpdir=/usr/local/apache-tomcat-7.0.52/temp
2060 Jps -Dapplication.home=/usr/local/server-jre-7u51 -Xms8m
$ /usr/local/server-jre-7u51/bin/jstat -gcutil 2039 5000
  S0     S1     E      O      P     YGC     YGCT    FGC    FGCT     GCT
  0.00 100.00  15.93  86.62  67.15     19    0.057     1    0.038    0.096
  0.00 100.00  15.93  86.62  67.15     19    0.057     1    0.038    0.096
  0.00 100.00  17.94  86.62  67.15     19    0.057     1    0.038    0.096
  0.00 100.00  17.94  86.62  67.15     19    0.057     1    0.038    0.096

ここで、S0やS1はSurvivor領域、EはEden領域である。JVMは時折このようなメルヘンチックな名前が付いているが、深みにはまると人として帰って来れなくなるので近づかない方が良い(偏見)。

Server JREの問題点

ここまででServer JREの内容がだいたい分かったので、これを実際にサーバにどうインストールして運用するか、ちょっと考えてみる。

まず、Server JREは以下のようなデメリットを持つため、これが問題になる環境では利用できないだろう。なおSolarisは無視してLinuxだけで考えます。

  • RPMパッケージが提供されない
    • JREJDKRPMパッケージが提供されているのだが、Server JREだけはtar.gz形式のみで、RPMパッケージが提供されない。そのためパッケージ管理したい人には不向きである。
  • 32bit版が提供されない
    • サーバ用途なら当然と言われればそうかもしれないが、Server JREは64bit版しか提供されていないため、32bit Linux環境には適用できない。ちなみにSolaris版は32bit版も提供されている。
  • 同梱ライブラリが不明瞭

結論から言うと、Server JREは、まだ積極的に使う必要は無いかな……というのが個人的な思い。

JVMのサーバーVM

最後にちょっと補足。

大変にややこしいのだが、JVM(Java Virtual Machine; Java仮想マシン)には、クライアントVMとサーバーVMという2つのモードがある。この「Server VM」は、今回Oracleが提供している「Server JRE」とは全くの無関係である。が、混乱を払拭するために、ここで敢えて説明しておく。

クライアントVMとサーバーVMというのは、Java実行時のJava仮想マシンの動作モードである。これはパフォーマンスに影響があるだけで、やれることは基本的に同じである。ご想像の通り、サーバーVMの方が長時間稼働するサーバーアプリケーションの実行に適したチューニングとなっている。一方、クライアントVMは起動の早さを優先している(らしい)。つまり、イメージとしては以下のようになる。

モード イメージ
クライアントVM 起動の早さとメモリ消費の小ささを優先。マリオカートで言えばヨッシー
サーバーVM 起動速度よりも、長時間稼働時のパフォーマンスを優先。マリオカートで言えばクッパ


これらのモードは、javaコマンド実行時(すなわちJava仮想マシン起動時)にOS環境によって自動的に決定される。お使いのマシンで、javaコマンドに-versionオプションを付ければ表示されるはずである。

ここでは、"Server VM"と表示されている。昨今のLinux環境ならば、相当貧弱な環境でなければ普通はServer VMが選択される(メモリの量、CPUの数、32bitか64bitか、あたりが判定基準だったはず)。なおこの選択候補は、JRE内の[/jre/lib//jvm.cfg]ファイルに記載されている。以下は64bit版Linuxの該当ファイルである。

$ cat /usr/local/jdk1.7.0_51/jre/lib/amd64/jvm.cfg
# Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved.
# ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
....(省略).....
# List of JVMs that can be used as an option to java, javac, etc.
# Order is important -- first in this list is the default JVM.
# NOTE that this both this file and its format are UNSUPPORTED and
# WILL GO AWAY in a future release.
#
# You may also select a JVM in an arbitrary location with the
# "-XXaltjvm=<jvm_dir>" option, but that too is unsupported
# and may not be available in a future release.
#
-server KNOWN
-client IGNORE
-hotspot ERROR
-classic WARN
-native ERROR
-green ERROR

このように、-serverと-clientが用意されているのが分かる。なおこれを見て分かるように、自動判定でClient VMが起動してしまうマシンでも、Java実行時に-serverオプションを付ければサーバーVMとして起動することができる。これはTomcat実行時によく使われるテクニックである。(Tomcat実行時のJAVA_OPTSに、-serverを明示的に付けておく)。

なおさらに蛇足だが、ここで出てくるHotSpotとは、Sun(じゃなかった、Oracleと言うべきか)のJVM高速化技術の名称である。だからOpenJDKでは、このHotSpotというのは出てこない。下図はOpenJDKの場合である。

Oracleの提供するJVMは、基本的にこのHotSpotが使われているので、Oracleの文書に「Java HotSpot」と出てきたら、それは全て読み飛ばして「Java」とだけ読んで理解してもあまり困らない。

余談だけで長くなってしまったが、これで以下の出力の意味がうっすら分かってきたのでは無かろうか。

OracleのJava
$ java -version
java version "1.7.0_51"
Java(TM) SE Runtime Environment (build 1.7.0_51-b13)
Java HotSpot(TM) 64-Bit Server VM (build 24.51-b03, mixed mode)
OpenJDK
$ java -version
java version "1.7.0_51"
OpenJDK Runtime Environment (rhel-2.4.4.1.el6_5-x86_64 u51-b02)
OpenJDK 64-Bit Server VM (build 24.45-b08, mixed mode)

参考情報

Javaの言語仕様およびVMのドキュメントについては、Oracleが無料でPDFを配布している(ただし英語)。

これらは、以前にピアソン桐原から出版されていた「Java言語仕様」と「Java仮想マシン仕様」である。英語が読めると、こういう風に情報源が大きく広がるんだなぁ、という好例。