class ファイルのバージョン

バージョンを調べる

java の .class ファイルには、バージョン情報が埋め込まれている。 古い実行環境では、新しいバージョンの class をロードすることができない。 class ファイルを生成する環境、つまり JDK のバージョンによって 生成できる class のバージョンも決まる。今のところ、新しい javac は 古い class を生成できるようである。

.class ファイルの情報は、javap コマンドで見ることができる。 例えば openjdk7 でコンパイルしたクラスを調べると、以下のような具合である。

% JAVA_HOME=/usr/local/openjdk7
% export JAVA_HOME
% java -version
openjdk version "1.7.0_21"
OpenJDK Runtime Environment (build 1.7.0_21-b11)
OpenJDK Server VM (build 23.21-b01, mixed mode)
% echo 'public class Empty {}' > Empty.java
% javac Empty.java
% javap -v Empty | grep version
  minor version: 0
  major version: 51

バージョンには メジャーバージョンとマイナーバージョンがあって、 JDK 7 では メジャーバージョンが 51 だということがわかる。

バージョン番号が知りたいだけであれば、バイナリエディタで見てもよい。 7 バイトめがメジャーバージョン、 8 バイトめがマイナーバージョン である。

(承前)
% od -h Empty.class | head -1
0000000      feca    beba    0000    3300    0d00    000a    0003    070a

「33」はもちろん16進なので、脳内で「3 * 16 + 3 = 51」と計算することになる。

バージョン一覧

class ファイルのバージョンは、javac 時に -target で指定することができる。 そこで、それぞれの target 指定時にバージョン番号がどうなるか、 openjdk8 で調べてみた。

(承前)
% JAVA_HOME=/usr/local/openjdk8
% export JAVA_HOME
% java -version
openjdk version "1.8.0"
OpenJDK Runtime Environment (build 1.8.0-b132)
OpenJDK Server VM (build 25.0-b70, mixed mode)
% for v in 1 2 3 4 5 6 7 8; do
> echo 1.$v
> javac -source 1.2 -target 1.$v -Xlint:-options Empty.java
> javap -v Empty | grep version
> done
1.1
  minor version: 3
  major version: 45
1.2
  minor version: 0
  major version: 46
1.3
  minor version: 0
  major version: 47
1.4
  minor version: 0
  major version: 48
1.5
  minor version: 0
  major version: 49
1.6
  minor version: 0
  major version: 50
1.7
  minor version: 0
  major version: 51
1.8
  minor version: 0
  major version: 52

1.2 以降は、きれいに一つずつ上がってきていることがわかる。

ちなみに 最初 -Xlint:-options を指定しないで試したところ、 警告が出た。

warning: [options] source value 1.2 is obsolete and will be removed in a future release
warning: [options] target value 1.4 is obsolete and will be removed in a future release

「将来的には -source 1.2 はサポートしませんよ」 「将来的には -target 1.4 はサポートしませんよ」 ということらしい。 この警告は openjdk8 から出るようになったようだ (openjdk7 では出ない)。

実行時・コンパイル時の挙動

新しいバージョンのクラスを古い実行環境にロードしようとすると、拒否られる。

(承前)
% JAVA_HOME=/usr/local/openjdk7
% export JAVA_HOME
% java -version
openjdk version "1.7.0_21"
OpenJDK Runtime Environment (build 1.7.0_21-b11)
OpenJDK Server VM (build 23.21-b01, mixed mode)
% javap -v Empty | grep version
  minor version: 0
  major version: 52
% java Empty
Exception in thread "main" java.lang.UnsupportedClassVersionError:
 Empty : Unsupported major.minor version 52.0
        at java.lang.ClassLoader.defineClass1(Native Method)
        at java.lang.ClassLoader.defineClass(ClassLoader.java:791)

(UnsupportedClassVersionError の行は便宜上改行しているが、実際は1行)

新旧のバージョンが混ざった状態でも、コンパイル時点では、 コンパイルできてしまうケースがある。

(承前)
% echo 'public class EmptyWrapper { Empty e; }' > EmptyWrapper.java
% javac EmptyWrapper.java
warning: ./Empty.class: major version 52 is newer than 51, the highest major
version supported by this compiler.
  It is recommended that the compiler be upgraded.
1 warning

(warning は便宜上改行しているが、実際は1行)

関連クラスがロードできない以上、事実上「実行できない」んだから、 コンパイルだけできたって意味ないじゃないか、と思うのだが、さてはて。

[$Revision: 1.2 $ $Date: 2014.05.17 06:03:11 $]
[EOF]