拡張子 | .class |
---|---|
MIMEタイプ | application/java-vm、application/x-httpd-java、application/x-java、application/java、application/java-byte-code、application/x-java-class、application/x-java-vm |
開発者 | サン・マイクロシステムズ |
種別 | バイトコード |
Javaクラスファイルは、Java仮想マシン (JVM) 上で実行可能なJavaバイトコードを含む(.class拡張子付きの)ファイルである。Javaクラスファイルは、Javaクラスを含むJava言語ソースファイル(.javaファイル)からJavaコンパイラによって作成される。ソースファイルに2つ以上のクラスがある場合、各クラスは分割されたクラスファイルとしてコンパイルされる。
JVMは多くのプラットフォームで利用可能であり、あるプラットフォームでコンパイルされたクラスファイルは他のプラットフォームのJVMでも実行できる。これによりJavaはクロスプラットフォームである。
2006年現在[update]、クラスファイルフォーマットの変更はJava Specification Request (JSR) 202の元で検討されている。[1]
Javaクラスファイル構造には10個の基本的な項目がある:
クラスファイルは次の(十六進法の)4バイトのヘッダで識別される : CA FE BA BE
(以下のテーブル内の最初の4つ)。このマジックナンバーの歴史についてジェームズ・ゴスリンが説明している。[2]
「私たちはSt Michael's Alleyと呼ばれる場所でよく昼食を取っていた。地元の言い伝えによると、深く暗黒の過去に、有名になる前のグレイトフル・デッドがそこでよく演奏していたという。そこは明確にグレイトフル・デッドにまつわる場所でとてもファンキーな場所だった。ジェリーが死んだ時、人々は少し仏教風な廟を建てることさえした。そこへ行っていた頃、私たちはその場所をCafe Deadと呼んだ。いつしか、それが十六進数であったことに気付いた。私はいくつかのファイルフォーマットを再度繕っていて、マジックナンバーが2つ必要になった。一つは永続的なオブジェクトファイルのため、もう一つはクラスのため。私はオブジェクトファイルフォーマット用にCAFEDEADを使用し、"CAFE" の後にふさわしい4文字の十六進の言葉をgrepして、BABEがヒットしたのでそれを使うことに決めた。その時は、それが歴史の片隅以外の場所に行くような、非常に重要で運命付けられているもののようには見えなかった。そうして、CAFEBABEはクラスファイルフォーマットとなり、CAFEDEADは永続的オブジェクトフォーマットとなった。しかし永続的オブジェクト機能は無くなってしまったため(結局はRMIに置き換えられた)、CAFEDEADも共に無くなった。」
クラスファイルは可変長の項目を含み、組み込まれたファイルのオフセット(またはポインタ)すら含まないため、通常は最初のバイトから最後まで順番に解析される。最も低レベルな点から、ファイルフォーマットはいくつかの基本的なデータ型の点から記述される:
これらの基礎的な型の一部は、前後関係に依存することで次に(文字列や浮動小数点数といった)上位レベルの値として再解釈される。 ワードが一直線に並ぶことは強制されないので、パディングバイトは常に利用されない。 クラスファイルの全てのレイアウトは以下のテーブル内のように見られる。
バイトオフセット | サイズ | 型あるいは値 | 記述 |
---|---|---|---|
0 | 4バイト | u1 = 0xCA 十六進 |
クラスファイルフォーマットに適合するファイルを識別するために使用されるマジックナンバー (CAFEBABE) |
1 | u1 = 0xFE 十六進 | ||
2 | u1 = 0xBA 十六進 | ||
3 | u1 = 0xBE 十六進 | ||
4 | 2バイト | u2 | 使用されるクラスファイルフォーマットのマイナーバージョン数 |
5 | |||
6 | 2バイト | u2 | 使用されるクラスファイルフォーマットのメジャーバージョン数。 JDK 1.1 = 45(0x2D 十六進)、 JDK 1.2 = 46(0x2E 十六進)、 JDK 1.3 = 47(0x2F 十六進)、 JDK 1.4 = 48(0x30 十六進)、 J2SE 5.0 = 49(0x31 十六進)、 JavaSE 6.0 = 50(0x32 十六進)、 JavaSE 7 = 51(0x33 十六進)、以降メジャーバージョン毎に1ずつ加算。 初期バージョン数の詳細についてはThe JavaTM Virtual Machine Specification 2nd editionの脚注を参照すること。 |
7 | |||
8 | 2バイト | u2 | 定数プールカウント、以下の定数プールテーブル内のエントリ数。このカウントは少なくとも1つは実在のエントリ数よりも大きい。以下の議論を参照すること。 |
9 | |||
10 | cpsize(変数) | テーブル | 定数プールテーブル、リテラル数、文字列、そしてクラスやメソッドへの参照といった項目を含む、可変長の定数プールエントリ。合計エントリ(定数テーブルカウント - 1)数を含む、1から始まり索引付けされる(脚注を参照すること)。 |
... | |||
... | |||
... | |||
10+cpsize | 2バイト | u2 | ビットマスクによるアクセスフラグ |
11+cpsize | |||
12+cpsize | 2バイト | u2 | 「クラス」型エントリの定数プールへのインデックスである、thisクラスを識別する |
13+cpsize | |||
14+cpsize | 2バイト | u2 | 「クラス」型エントリの定数プールへのインデックスである、スーパークラスを識別する |
15+cpsize | |||
16+cpsize | 2バイト | u2 | インタフェースカウント、以下のインタフェーステーブル内のエントリの数 |
17+cpsize | |||
18+cpsize | isize(変数) | テーブル | インタフェーステーブル、可変長インタフェースの配列 |
... | |||
... | |||
... | |||
18+cpsize+isize | 2バイト | u2 | 変数カウント、以下のフィールド内のエントリの数 |
19+cpsize+isize | |||
20+cpsize+isize | fsize(変数) | テーブル | フィールドテーブル、フィールドの可変長配列 |
... | |||
... | |||
... | |||
20+cpsize+isize+fsize | 2バイト | u2 | メソッドカウント、以下のメソッドテーブル内のエントリ数 |
21+cpsize+isize+fsize | |||
22+cpsize+isize+fsize | msize(変数) | テーブル | メソッドテーブル、メソッドの可変長配列 |
... | |||
... | |||
... | |||
22+cpsize+isize+fsize+msize | 2バイト | u2 | 属性カウント、以下の属性テーブル内のエントリ数 |
23+cpsize+isize+fsize+msize | |||
24+cpsize+isize+fsize+msize | asize(変数) | テーブル | 属性テーブル、属性の可変長配列 |
... | |||
... | |||
... |
Cは複雑な、構造体内の可変長配列をサポートしていないため、以下のコードはコンパイル不可能で、デモンストレーションとしてのみ役立つ。
struct Class_File_Format {
u4 magic_number;
u2 minor_version;
u2 major_version;
u2 constant_pool_count;
cp_info constant_pool[constant_pool_count - 1];
u2 access_flags;
u2 this_class;
u2 super_class;
u2 interfaces_count;
u2 interfaces[interfaces_count];
u2 fields_count;
field_info fields[fields_count];
u2 methods_count;
method_info methods[methods_count];
u2 attributes_count;
attribute_info attributes[attributes_count];
}
定数プールは、大部分のリテラル定数値がストアされる場所である。ここには全ての種類の数、文字列、識別子名、クラスおよびメソッドへの参照、そしてタイプデスクリプタといった値が含まれる。定数テーブル内の明確な定数への全てのインデックスや参照は、インデックスがテーブル内の最初の定数を1に指し示す(インデックス値0は不正)、16ビット(u2型)数で与えられる。
ファイルフォーマット開発中の歴史的な選択のため、定数テーブル内の定数の数は、テーブルの先にある定数テーブルカウントと実際には同じではない。最初に、テーブルは(0ではなく)1で始まるようインデックス付けされ、そのためにカウントは実際には最大インデックスとして解釈されるべきである。加えて、定数の2つの型(longおよびdouble)は連続したスロットを取る。しかし二つ目のそのスロットは決して直接使用されない幻のインデックスである。
定数テーブル内の各項目(定数)の型は、最初のバイトであるタグで識別される。このタグに続く数とこれらの解釈は、それ以降タグの値に左右される。正当な定数型とそのタグは以下の通りである:
タグバイト | 追加のバイト | 定数の解説 |
---|---|---|
1 | 2+xバイト (変数) |
UTF-8 (Unicode) 文字列 : 直接続くエンコードされた文字列内の数を示す、16ビット数の接頭辞が付くキャラクタ文字列。エンコーディングは実際にはUTF-8ではなく、Unicode標準のエンコーディング形式のわずかな修正を含むことに注意すること。 |
3 | 4バイト | Integer : ビッグエンディアンフォーマットによる符号付き32ビット2の補数 |
4 | 4バイト | Float : 32ビット単精度IEEE 754浮動小数点数 |
5 | 8バイト | Long : ビッグエンディアンフォーマットによる符号付き64ビット2の補数(定数テーブルの2つのスロットを占める) |
6 | 8バイト | Double : 64ビット倍精度IEEE 754浮動小数点数(定数テーブルの2つのスロットを占める) |
7 | 2バイト | クラス参照 : (内部フォーマットによる)完全修飾型クラス名を含むUTF-8文字列による定数テーブル内のインデックス(ビッグエンディアン) |
8 | 2バイト | 文字列参照 : UTF-8による定数プール内のインデックス(ビッグエンディアン) |
9 | 4バイト | フィールド参照 : 定数プール内にある2つのインデックス、最初はクラス参照で次は名前および型の記述(ビッグエンディアン) |
10 | 4バイト | メソッド参照 : 定数プール内にある2つのインデックス、最初はクラス参照で次は名前および型の記述(ビッグエンディアン) |
11 | 4バイト | インタフェース参照 : 定数プール内にある2つのインデックス、最初はクラス参照で次は名前および型の記述(ビッグエンディアン) |
12 | 4バイト | 名前および型の記述 : UTF-8による定数プール内のインデックス、最初は名前(識別子)を表し次は特別にエンコードされた型 |
整数定数型はintegerとlongの2つしかない。boolean、byte、そしてshortといった上位レベル言語で見られる他の定数型は、integer定数として表されなければならない。
完全修飾されたJavaのクラス名は、「java.lang.Object」のように慣例的にドットで区分けされる。しかしながら低レベルなクラス参照定数内においては、内部形式は「java/lang/Object」のように、代わりにスラッシュを使用する。
Unicode文字列は、「UTF-8文字列」とあだ名されているにも係らず、実際にはUnicode標準に従ってエンコードされておらずそれと似たような形式である。それら2つには違いがある(完全な議論についてはUTF-8を参照すること)。最初に、コードポイントU+0000は、標準的な1バイト00
にエンコードする代わりに、2バイトのシーケンスC0 80
としてエンコードされる。
2つ目の違いは、(U+10000および上記としてBasic Multilingual Planeの外側にある)補足する文字が、UTF-8を用いて直接エンコードするのではなくUTF-16に似たサロゲートペア構造を用いてエンコードすることである。この場合、2つのサロゲートの各々がUTF-8において分割してエンコードされる。例えばU+1D11Eは、4バイトのUTF-8エンコーディングF0 9D 84 9E
ではなく、6バイトのシーケンスED A0 B4 ED B4 9E
としてエンコードされる。