Javaのバージョンの様々な取得方法

Javaのバージョン情報を取得するとき、多くの人が思い浮かべるのは`java --version`かと思います。
実は、javaのバージョン情報を取得する方法はほかにもいくつかあります。
本記事では、Javaのバージョン情報の取得方法とその出力をご紹介します。

# 前提

## 使用jdk

今回使用するjdkはTemurinのLinux X64 21.0.6+7-LTSです。
こちらのサイトからダウンロードできます。
https://adoptium.net/temurin/releases/

直リンクも張っておきます。
※クリックしたらブラウザによってはダウンロードが始まります。
https://github.com/adoptium/temurin21-binaries/releases/download/jdk-21.0.6%2B7/OpenJDK21U-jdk_x64_linux_hotspot_21.0.6_7.tar.gz

## 注意事項

実は、バージョン情報はある程度ベンダーが自由に書き換えることができます。
これにより、同じようにバージョン情報を取得しようとしても形式や情報量が異なる可能性があります。
本記事は基本的にTemurinを使って紹介していますので、その点をご留意ください。

# バージョン取得方法

## 単体コマンド編

### java --verison

```
$ java --version
openjdk 21.0.6 2025-01-21 LTS
OpenJDK Runtime Environment Temurin-21.0.6+7 (build 21.0.6+7-LTS)
OpenJDK 64-Bit Server VM Temurin-21.0.6+7 (build 21.0.6+7-LTS, mixed mode, sharing)
```

基本的にJavaのバージョン何使ってるか教えてと聞かれたらこれを見せれば大丈夫です。

参考：jdk8以前ではこんな感じでエラーになります。
```
$ java --version
Unrecognized option: --version
Error: Could not create the Java Virtual Machine.
Error: A fatal exception has occurred. Program will exit.
```

### java -verison

```
openjdk version "21.0.6" 2025-01-21 LTS
OpenJDK Runtime Environment Temurin-21.0.6+7 (build 21.0.6+7-LTS)
OpenJDK 64-Bit Server VM Temurin-21.0.6+7 (build 21.0.6+7-LTS, mixed mode, sharing)
```

`--verison`か`-version`かの違い。
よく見ると1行目の出力形式が異なります。

参考：jdk8では`--version`ではなくこちらを使います。
```
$ java -version
openjdk version "1.8.0_442"
OpenJDK Runtime Environment (Temurin)(build 1.8.0_442-b06)
OpenJDK 64-Bit Server VM (Temurin)(build 25.442-b06, mixed mode)
```

### java -fullversion

```
$ java -fullversion
openjdk full version "21.0.6+7-LTS"
```

fullっていってるのに情報量少な目。

### java --full-version

```
$ java --full-version
openjdk 21.0.6+7-LTS
```

fullっていってるのに情報量少な目。

### java -showversion

```
$ java -showversion Foo
openjdk version "21.0.6" 2025-01-21 LTS
OpenJDK Runtime Environment Temurin-21.0.6+7 (build 21.0.6+7-LTS)
OpenJDK 64-Bit Server VM Temurin-21.0.6+7 (build 21.0.6+7-LTS, mixed mode, sharing)
Foo Program
```

なにかアプリを実行するときに`-showversion`をつけておくと、その実行前にバージョン情報が出力されます。
出力内容は`java -version`と同じ。

### java --show-version

```
$ java --show-version Foo
openjdk 21.0.6 2025-01-21 LTS
OpenJDK Runtime Environment Temurin-21.0.6+7 (build 21.0.6+7-LTS)
OpenJDK 64-Bit Server VM Temurin-21.0.6+7 (build 21.0.6+7-LTS, mixed mode, sharing)
Foo Program
```
使い方は`-showversion`と同じ。
出力内容は`java --version`と同じ。

### java -Xinternalversion

```
$ java -Xinternalversion
OpenJDK 64-Bit Server VM (21.0.6+7-LTS) for linux-amd64 JRE (21.0.6+7-LTS), built on 2025-01-21T00:00:00Z by "admin" with gcc 11.3.0
```

いつ、だれが、どのコンパイラでビルドしたか分かります。

ただし、自身でビルドしたjdkで見てみると「だれが」の情報がないので、Temurinは何らかの方法で入るようにしている可能性あります。
```
$ java -Xinternalversion
OpenJDK 64-Bit Server VM (21.0.5+5) for linux-amd64 JRE (21.0.5+5), built on 2025-01-20T11:39:10Z with gcc 11.4.0
```

## その他編

### releaseファイル

jdkのディレクトリの直下に`release`というファイルがあるます。
中にはバージョンを含め、そのjdkの様々な情報が入っています。
```
$ cat $JAVA_HOME/release
IMPLEMENTOR="Eclipse Adoptium"
IMPLEMENTOR_VERSION="Temurin-21.0.6+7"
JAVA_RUNTIME_VERSION="21.0.6+7-LTS"
JAVA_VERSION="21.0.6"
JAVA_VERSION_DATE="2025-01-21"
LIBC="gnu"
MODULES="java.base java.compiler java.datatransfer java.xml java.prefs java.desktop java.instrument java.logging java.management java.security.sasl java.naming java.rmi java.management.rmi java.net.http java.scripting java.security.jgss java.transaction.xa java.sql java.sql.rowset java.xml.crypto java.se java.smartcardio jdk.accessibility jdk.internal.jvmstat jdk.attach jdk.charsets jdk.internal.opt jdk.zipfs jdk.compiler jdk.crypto.ec jdk.crypto.cryptoki jdk.dynalink jdk.internal.ed jdk.editpad jdk.hotspot.agent jdk.httpserver jdk.incubator.vector jdk.internal.le jdk.internal.vm.ci jdk.internal.vm.compiler jdk.internal.vm.compiler.management jdk.jartool jdk.javadoc jdk.jcmd jdk.management jdk.management.agent jdk.jconsole jdk.jdeps jdk.jdwp.agent jdk.jdi jdk.jfr jdk.jlink jdk.jpackage jdk.jshell jdk.jsobject jdk.jstatd jdk.localedata jdk.management.jfr jdk.naming.dns jdk.naming.rmi jdk.net jdk.nio.mapmode jdk.random jdk.sctp jdk.security.auth jdk.security.jgss jdk.unsupported jdk.unsupported.desktop jdk.xml.dom"
OS_ARCH="x86_64"
OS_NAME="Linux"
SOURCE=".:git:1180e1bcc938"
BUILD_SOURCE="git:aaf9cbd0641cf21daa951c69ef6d7183b3af9e99"
BUILD_SOURCE_REPO="https://github.com/adoptium/temurin-build.git"
SOURCE_REPO="https://github.com/adoptium/jdk21u.git"
FULL_VERSION="21.0.6+7-LTS"
SEMANTIC_VERSION="21.0.6+7"
BUILD_INFO="OS: Linux Version: 6.8.0-1020-azure"
JVM_VARIANT="Hotspot"
JVM_VERSION="21.0.6+7-LTS"
IMAGE_TYPE="JDK"
```
ちなみに、`BUILD_SOURCE`以降の情報はTemurinが独自で追加しているので他ベンダーだと記載がない場合も大いにあるし、ほかの情報が入っている可能性もある。

### システムプロパティ

デフォルトで定義されているシステムプロパティの中にはバージョン情報を含むものがあります。
それらは以下のようなプログラムで取得できます。

```java GetSystemProperties.java
class GetSystemProperties {
  public static void main(String[] args) {
    System.out.println("java.version = "+System.getProperty("java.version"));
    System.out.println("java.runtime.version = "+System.getProperty("java.runtime.version"));
    System.out.println("java.class.version = "+System.getProperty("java.class.version"));
    System.out.println("java.specification.version = "+System.getProperty("java.specification.version"));
  }
}
```
```
$ java GetSystemProperties
java.version = 21.0.6
java.runtime.version = 21.0.6+7-LTS
java.class.version = 65.0
java.specification.version = 21
```

他にもバージョンに関わるシステムプロパティはあります。
すべてのシステムプロパティを確認したい場合は`System.getProperties()`を使ってみてください。

### コアファイル

何らかの理由でJVMがクラッシュしたときにコアファイルが生成されることがあります。
この方法はOSやCPUアーキテクチャによっては使用できない場合もありますが、少なくともX64のLinuxでは使用可能です。

まず、生成されたコアファイルを取得します。
ここではカレントディレクトリに置きます。
```
$ ls -l core
-r-------- 1 tabatad root 400789504 Jan 28 14:34 core
```

以下のコマンドで、実行時にjavaコマンドがアロケートされていた仮想アドレスを取得します。
3つ出てきますが、1つ目の一番左のアドレスをメモしておきます。
今回の場合だと`0x000055988ca65000`。
※パスの部分は修正しています。
```
$ readelf -n core | grep \/java -1
    0x000055988ca65000  0x000055988ca66000  0x0000000000000000
        /hoge/jdk-21.0.6+7/bin/java
    0x000055988ca67000  0x000055988ca68000  0x0000000000000001
        /hoge/jdk-21.0.6+7/bin/java
    0x000055988ca68000  0x000055988ca69000  0x0000000000000002
        /hoge/jdk-21.0.6+7/bin/java
    0x00007f0417000000  0x00007f0417c97000  0x0000000000000001
```

次に以下のコマンドを実行して、コアファイル内のどのオフセットに実行時の仮想メモリがダンプされているか取得します。
grepは1つ前の手順でメモしたアドレスでかけます。
3つ並んだ16進数のうち、一番左をメモしておきます。
今回の場合だと`0x000000000fb19000`。
```
$ readelf -l core | grep 0x000055988ca65000
  LOAD           0x000000000fb19000 0x000055988ca65000 0x0000000000000000
```

次に以下のコマンドを実行して、コアファイル内の指定したオフセット（1つ前の手順で取得）から0x1000バイトを16進数で見ます。odコマンドのオプションにより、ASCII文字で表現できるバイトはASCII文字で出力されます。
```
$ od -j 0x000000000fb19000 -N 0x1000 -A x -t x1z -v core
fb19000 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00  >.ELF............<
（中略）
fb19d40 63 6b 65 64 20 75 70 20 25 73 3a 20 25 73 00 6f  >cked up %s: %s.o<
fb19d50 70 65 6e 6a 64 6b 00 6a 61 76 61 00 30 2e 30 00  >penjdk.java.0.0.<
fb19d60 32 31 2e 30 2e 36 2b 37 2d 4c 54 53 00 00 00 00  >21.0.6+7-LTS....<
（後略）
```
大量の16進数が表示されますが、一番右の部分を眺めているとJavaのバージョンらしき文字列が見当たるかもしれません。

この方法は、コアファイルのみからそのコアが生成されたJavaのバージョンを特定できる可能性がある点がメリットです。
（現実的にはどのjavaがコアを生成したか分からないというシチュエーションはおおよそないはず）

# さいごに

本記事では、様々なJavaのバージョン情報の取得方法とその出力をご紹介しました。
Javaのバージョンごとに処理を分岐するときなどのご参考になれば幸いです。

ここに記載していない方法をご存じの方はぜひコメントにご記載ください！