この記事の目的
- 「Javaの環境構築」で絶対にハマったり、錯綜する情報にいつも惑わされる人に向けた記事です。
- 2017年以降のJavaは、移り変わりが激しい世界になりました。このことを認識し、「軸となる考え方」や「重要な動向」を把握できるように、調べた情報をまとめました。
- **「5年ぶりに(Java|JVM言語)触るんだけど環境周りが全然わからん」とか、「CorrettoとかAdoptOpenJDKとか、みんな何を言っているんだ」**という人(つまりちょっと前の筆者)が、「今後に渡って2度とハマらないための基礎知識を得られること」を目的にしています。以下の3本立てです:
- ざっくりとした歴史
- 2020年におけるローカル開発環境構築
- 情報源と参考URL
- APIや言語仕様の変更点には触れません(他の良い記事があります)。
I. ざっくりとした歴史
- 2017年9月、OracleはJavaのリリース・モデルの変更を発表し、同時に「Oracle JDKの有償機能をOpenJDKで公開」することを発表した。すなわちJavaはオープン化されることになった(有償化ではなく)。
- 以前から有償契約でサポートを受けていたクライアントは、引き続き有償サポートを受けられる。この場合に限り、JDKは"Open"ではない「Oracle JDK」として提供され、プロダクション環境での利用が有償となる(ここが誤解されがち)。
リリースサイクルについて
-
毎年3月と9月にフィーチャー・リリースが提供され、メジャーバージョンが上がっていく。Ex: Java9 => 2017年9月、Java10 => 2018年3月、Java11 => 2018年9月、...
-
全フィーチャー・リリースがOracle JDKのLTS(Long Term Support)というわけではない。Java9以降では、Java11, Java17がLTS対象。それ以外はnon-LTS。
-
詳しくは、Oracle Java SE サポート・ロードマップを参照。
「8とそれ以降」
- Java8は「リリース・モデル変更前の最後のバージョン」という位置にいる。Java8自体もオープン化され、かつOracle JDKのLTS対象になっている。
- Java8からJava9に上がる際、Project Jigsawと呼ばれるモジュール化が採用され、設計に比較的大きな変更が入った。
- つまり、「Java8以前とJava9以降」ではリリース・モデルの面でも、互換性の面でも大きな隔たりがある。移行に際しては両面を(混同することなく)意識したほうが良い。
ディストリビューションの分化
-
オープンソース化したことで、複数ベンダーがJDKをビルド、発表できるようになった。OpenJDKのソースを取り巻き、Linuxのように各社のディストリビューションが複数存在する状況が生まれた。
(出典: 最適なOpenJDKディストリビューションの選び方) -
2020年現在、主要なディストリビューションは以下:
- Oracle OpenJDK (by Oracle)
- Red Hat OpenJDK (by Red Hat)
- Azul Zulu (by Azul Systems)
- SapMachine (by SAP)
- Liberica JDK (by BellSoft)
- Amazon Corretto (by Amazon)
- AdoptOpenJDK (Community Based)
-
各社のOpenJDKディストリビューションは、「latestだけ」「OracleのLTS対象だけ」などバージョンを絞って公開・サポートすることがある。
- 例えば2020年1月現在、Oracle OpenJDKは最新の13をGA(General-Availability Release)として公開。他バージョンもダウンロードは可能だが、使用は非推奨。
- 同じく2020年1月現在、Amazon Correttoは8と11を公開。
-
ディストリビューションにお墨付きを与えるため、Technology Compatibility Kit(TCK)という一連のテストがOpenJDKコミュニティ(Oracleと要契約)によって用意されている。TCKをパスしたディストリビューションは、“Java SE compatible”と名乗ることができる
-
AdoptOpenJDKのように、TCKをpassしていないが比較的普及しているディストリビューションもある
どう選べばいいの?
-
サポート状況や、各ディストリビューションを取り巻くエコシステムで判断するのが現実的。
- 例えば、Zuluのような有償ライセンス / サポートがあるか?
- 例えば、インストーラはきちんと準備されていて、使いやすいか?
- 例えば、公式Dockerイメージは用意されているか?
-
とはいえ、現状目立った差は少なく、選びづらい面もある。これから特徴が出てくるかもしれない。
II. 2020年におけるローカル開発環境構築
Iに書いたような状況なので、定まったベストはなく、しかもすぐに陳腐化する傾向にある。
=> SDKMAN!が現状のベストプラクティス。(2020.1.10 追記)
ここでは、「コマンドラインのみでインストールが終わること」「複数バージョンを切り替え可能な状態にすること」を重視し、以下2パターンの構築手順を例示する:
- パターン1: SDKMAN!(推奨)
- パターン2: Homebrew & jEnv
パターン1: SDKMAN!によるインストール
SDKMAN!は、Groovyの複数バージョン管理ツールであるGVMを前身とする。JVM言語やそのビルドツールを中心に多種多様なツールの複数バージョンの管理を実現してくれる。
SDKMAN!自体のインストールは公式を参照。導入後、Javaのインストールは以下のコマンドで完結する:
sdk install java
このときインストールされるディストリビューションは、2020年1月現在はAdoptOpenJDKの11。
その他のディストリビューションに関しても、list
, install
, default
の3つのサブコマンドで自由に確認・切替えできる。バージョンやディストリビューションの指定はリスト中のIdentifierを使用する。
sdk list java
================================================================================
Available Java Versions
================================================================================
Vendor | Use | Version | Dist | Status | Identifier
--------------------------------------------------------------------------------
AdoptOpenJDK | | 13.0.1.j9 | adpt | | 13.0.1.j9-adpt
| | 13.0.1.hs | adpt | | 13.0.1.hs-adpt
| | 12.0.2.j9 | adpt | | 12.0.2.j9-adpt
| | 12.0.2.hs | adpt | | 12.0.2.hs-adpt
| | 11.0.5.j9 | adpt | | 11.0.5.j9-adpt
| >>> | 11.0.5.hs | adpt | installed | 11.0.5.hs-adpt
| | 8.0.232.j9 | adpt | | 8.0.232.j9-adpt
| | 8.0.232.hs | adpt | | 8.0.232.hs-adpt
Amazon | | 11.0.5 | amzn | | 11.0.5-amzn
| | 8.0.232 | amzn | | 8.0.232-amzn
| | 8.0.202 | amzn | | 8.0.202-amzn
Azul Zulu | | 13.0.1 | zulu | | 13.0.1-zulu
| | 12.0.2 | zulu | | 12.0.2-zulu
...(中略)...
SAP | | 12.0.2 | sapmchn | | 12.0.2-sapmchn
| | 11.0.4 | sapmchn | | 11.0.4-sapmchn
================================================================================
なお、2つ目以降のJDKインストール時に、そのディストリビューションをデフォルトにするかどうかプロンプトで尋ねられる。
sdk install java 13.0.1-open # Oracle OpenJDK 13
Downloading: java 13.0.1-open
...(中略)...
Installing: java 13.0.1-open
Done installing!
Do you want java 13.0.1-open to be set as default? (Y/n): Y
Setting java 13.0.1-open as default.
デフォルトに設定すれば、その時点でJavaのバージョンは切り替わる。パスの設定等は一切不要。試しにjshellの起動確認をしてみる。
jshell
| JShellへようこそ -- バージョン13.0.1
| 概要については、次を入力してください: /help intro
jshell>
jshellはJava9から追加されたJavaのREPLである。他の言語でお馴染みかもしれないが、標準APIのちょっとした動作確認などに重宝する。/exit
で終了できるので、バージョンを戻して再実行してみる。
sdk default java 11.0.5.hs-adpt
Default java version set to 11.0.5.hs-adpt
jshell
| JShellへようこそ -- バージョン11.0.5
| 概要については、次を入力してください: /help intro
jshell>
バージョンが切り替ることを確認できた。
パターン2: Homebrew & jEnvによるインストール
パターン2: Homebrew & jEnvによるインストール
SDKMAN!に比べて煩雑で、お勧めできない。裏側でどのようにパスが通されているかなどの参考にはなるかも
HomebrewでのJDKインストール
- 比較的最近(恐らく2019年11月)、Formulaeに
openjdk
が追加された。したがって、brew install openjdk
でインストール可能。 - この時インストールされるディストリビューションは、Oracle OpenJDK。
- Java8やそれ以前をインストールする必要がある場合、または他のディストリビューションを利用したい場合は、現状Homebrew Caskを使うことになる(この記事では割愛する)。
- サポートされているFormulaeはここで検索するのが早い => https://formulae.brew.sh/formula/
インストールに成功するとCaveats(警告)が出ていることに気づく。以下はCatalina+zshで試したときのメッセージ。
==> Pouring openjdk-13.0.1+9.catalina.bottle.tar.gz
==> Caveats
For the system Java wrappers to find this JDK, symlink it with
sudo ln -sfn /usr/local/opt/openjdk/libexec/openjdk.jdk /Library/Java/JavaVirtualMachines/openjdk.jdk
openjdk is keg-only, which means it was not symlinked into /usr/local,
because macOS already provides this software and installing another version in
parallel can cause all kinds of trouble.
If you need to have openjdk first in your PATH run:
echo 'export PATH="/usr/local/opt/openjdk/bin:$PATH"' >> ~/.zshrc
さらっと書いてあるが、どのパラグラフも重要。環境によってはメッセージの細部や取るべき対応が違うこともある。
For the system Java wrappers to find this JDK, symlink it with...
"system Java wrappers"が何を指すか明確でないが、後述するjava_home
コマンドなどを動作させるために、シンボリックリンクの作成が必要。
openjdk is keg-only, which means it was not symlinked into /usr/local,...
/usr/local
以下にsymlinkされていないということなので、つまりこの時点でターミナルでjava
と打ってもここでインストールしたOpenJDKが直ちに動作するわけではないと言っている。
If you need to have openjdk first in your PATH run:...
ここでインストールしたOpenJDKがパスとして最初に検索されるようにするためには、シェルの起動時にパスを追加する必要がある。
ただ、今回は複数バージョンのJDKを別ツールで管理する方針なので、Javaのパスを直接~/.zshrc
に書くことは避ける。
以上を踏まえて、最初のメッセージが推奨する、シンボリックリンクの作成だけを実行する:
sudo ln -sfn /usr/local/opt/openjdk/libexec/openjdk.jdk /Library/Java/JavaVirtualMachines/openjdk.jdk
こうすれば、以下のコマンドが動作するようになってくれるはずだ:
java --version
openjdk 13.0.1 2019-10-15
OpenJDK Runtime Environment (build 13.0.1+9)
OpenJDK 64-Bit Server VM (build 13.0.1+9, mixed mode, sharing)
/usr/libexec/java_home
/Library/Java/JavaVirtualMachines/openjdk.jdk/Contents/Home
jEnvへの登録
pyenvのように、複数バージョンのJDKの管理を楽にしてくれるツール。インストール手順は公式に譲る。brew install
の実行後、パスを通す必要がある。
例えばpyEnvだとpyenv install 3.5.0
などのコマンドでPythonをインストールできる。だがここまでの操作からもわかるように、jEnv自体にJDKをダウンロードしてくる機能はない。jenv add
コマンドが引数として要求するのは、「ダウンロード済みのJDKのJAVA_HOME
へのフルパス」だ。このパスは先述のjava_home
コマンドで参照できる。
/usr/libexec/java_home
/Library/Java/JavaVirtualMachines/openjdk.jdk/Contents/Home
あとはこれを利用してJDKをjEnvに追加してやれば良い:
jenv add `/usr/libexec/java_home`
openjdk64-13.0.1 added
13.0.1 added
13.0 added
複数バージョンのインストール
上記の状態から、例えば、Oracle OpenJDKの11を追加でインストールする場合は以下のような手順になる:
# 1. JDKをインストールする
brew install openjdk@11
# 2. JavaVirtualMachines配下にsymlinkを作成する
# (brew install時にプロンプトに出てくるコマンドをコピペする)
sudo ln -sfn /usr/local/opt/openjdk@11/libexec/openjdk.jdk /Library/Java/JavaVirtualMachines/openjdk-11.jdk
# 3. jEnvの管理対象に加える
jenv add `/usr/libexec/java_home -v 11`
jEnvが複数バージョンを管理できるようになったことの確認と、jshellの起動確認をしてみる。
jenv versions
* system
11.0
11.0.5
13.0
13.0.1
openjdk64-11.0.5
openjdk64-13.0.1
バージョンを13に設定する。
jenv global 13.0
jenv versions
system
11.0
11.0.5
* 13.0
13.0.1
openjdk64-11.0.5
openjdk64-13.0.1
jshell
| JShellへようこそ -- バージョン13.0.1
| 概要については、次を入力してください: /help intro
jshell>
続いてバージョンを切り替えて同じことをしてみる。
jenv global 11.0
jenv versions
system
* 11.0
11.0.5
13.0
13.0.1
openjdk64-11.0.5
openjdk64-13.0.1
jshell
| JShellへようこそ -- バージョン11.0.5
| 概要については、次を入力してください: /help intro
バージョンが切り替ることを確認できた。
ここまでやっておけば、ある程度ディストリビューションやバージョンの追加にも対応できるローカル開発環境と言えるはず。
III. 情報源と参考URL
以上述べてきたような経緯があるので、選択したディストリビューションによって開発者が参照すべき情報は異なってくる。
横断的に現状を把握できるページが見つかれば望ましいが、現状は、まずOpenJDKの開発をリードするOracle公式のロードマップを参照するのが良いだろう。
Oracle Java SE サポート・ロードマップ
https://www.oracle.com/technetwork/jp/java/eol-135779-ja.html
Red Hatなど、ベンダーが自社ディストリビューションのライフサイクルやポリシーを公開している場合もある。
OpenJDK Life Cycle and Support Policy
https://access.redhat.com/articles/1299013
その他 参考URL
(本記事はほとんど以下の記事からの抜粋と要約なので、より深く知りたい方はぜひ!)
[JDKの新しいリリース・モデルおよび提供ライセンスについて]
(https://www.oracle.com/technetwork/jp/articles/java/ja-topics/jdk-release-model-4487660-ja.html)
[Project Jigsaw]
(https://openjdk.java.net/projects/jigsaw/)
[これからのJDK 何を選ぶ?どう選ぶ? (v1.2)]
(https://www.slideshare.net/TakahiroYamada3/how-to-choose-jdk-20191101)[^1]
[最適なOpenJDKディストリビューションの選び方]
(https://www.slideshare.net/TakahiroYamada3/how-to-choose-the-best-openjdk-distribution-201905)
[「Java 有償化」で誤解する人になるべく分かりやすく説明するためのまとめ]
(https://togetter.com/li/1343743)
[OpenJDK - Wikipedia]
(https://ja.wikipedia.org/wiki/OpenJDK)
[Oracle JDK vs. OpenJDK builds comparison]
(https://devexperts.com/blog/oracle-jdk-vs-openjdk-builds-comparison/)
関連Qiita記事1
OpenJDKと各種JDKディストリビューションの情報源まとめ #minjava