JavaのJDK/JRE/Server JREの使い分け
先日、OracleのWebページからJDKとJREをダウンロードしようとしたら、「Server JRE」というのが用意されていることに気がついた。
このServer JREとは、いったい何じゃらほい? というのが今回のテーマ。なお基本的にLinuxサーバ環境で考えます(Windows, Solarisは無視)。
JDKとJRE
はじめに、複雑怪奇なJavaパッケージについて少し解説しておく。「Javaをインストール」という場合には、開発キットであるJDKとランタイムであるJRE、どちらをインストールするかが問題になる。しかしこの日記を読んでいるような人ならばJava開発者であろうから、JDKを入れてしまえばほぼ間違いなく問題は解決する。
なぜなら、JDKはJREを内包しているので、JDKをインストールすれば自動的にJREも入るからである。というわけで開発者なら、何も考えずにJDKを入れてしまえば良い(動作検証のため複数バージョンのJRE混在とか、いろいろ問題は山積みだが、とりあえずその辺は置いておく)。
Server JRE
2013年4月にリリースされたJava SE 7u21から、JDKとJREに加えて、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ディレクトリのファイルリストの差を書いておく。
- JREの場合
[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パッケージが提供されない
- 32bit版が提供されない
- 同梱ライブラリが不明瞭
- Server JREはまだ登場したばかりなので、Oracleは「同梱ライブラリは今後精査していく」とアナウンスしている(https://blogs.oracle.com/security/entry/maintaining_the_security_worthiness_of)。つまり、あなたが必要とするライブラリがある日突然同梱されなくなる可能性がある。
結論から言うと、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/
$ 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を配布している(ただし英語)。
- http://docs.oracle.com/javase/specs/
- The Java Language Specification, Java SE 7 Edition
- The Java Virtual Machine Specification, Java SE 7 Edition
これらは、以前にピアソン桐原から出版されていた「Java言語仕様」と「Java仮想マシン仕様」である。英語が読めると、こういう風に情報源が大きく広がるんだなぁ、という好例。