| Javaクラスファイル | |
|---|---|
| インターネットメディアの種類 | アプリケーション/java-vm、アプリケーション/x-httpd-java、アプリケーション/x-java、アプリケーション/java、アプリケーション/java-byte-code、アプリケーション/x-java-class、アプリケーション/x-java-vm |
| 開発者 | サン・マイクロシステムズ |
Javaクラスファイルは、 Java仮想マシン(JVM)上で実行可能なJavaバイトコードを含むファイル(ファイル名拡張子は.class )です。Javaクラスファイルは通常、Javaクラスを含むJavaプログラミング言語ソースファイル(.javaファイル)からJavaコンパイラによって生成されます(他のJVM言語を使用してクラスファイルを作成することもできます)。ソースファイルに複数のクラスが含まれている場合、各クラスは個別のクラスファイルにコンパイルされます。したがって、このファイルは単一のクラスのバイトコードを含むため、 .classファイルと呼ばれます。
JVMは多くのプラットフォームで利用可能であり、あるプラットフォームでコンパイルされたクラスファイルは、別のプラットフォームのJVMでも実行できます。これにより、Javaアプリケーションはプラットフォームに依存しなくなります。
2006年12月11日、 Java仕様要求(JSR)202に基づいてクラスファイル形式が変更されました。[ 1 ]
Java クラス ファイル構造には 10 個の基本セクションがあります。
0xCAFEBABEクラスファイルは、以下の4バイトのヘッダー(16進数)によって識別されますCA FE BA BE(下表の最初の4つのエントリ)。このマジックナンバーの歴史は、パロアルトのレストランを例に挙げてジェームズ・ゴスリングによって説明されています。[ 2 ]
よくセント・マイケルズ・アレーという店でランチをしていたんです。地元の言い伝えによると、グレイトフル・デッドがブレイクする前の暗い昔、そこで演奏していたらしいんです。そこはすごくファンキーな場所で、まさにグレイトフル・デッドらしい場所でした。ジェリーが亡くなった時には、小さな仏教風の祠まで建てられました。私たちがそこに通っていた頃は、カフェ・デッドと呼んでいました。いつの間にか、これが16進数だと気づきました。ファイル形式のコードを改修していて、2つのマジックナンバーが必要でした。1つは永続オブジェクトファイル用、もう1つはクラス用です。オブジェクトファイル形式にはCAFEDEADを使い、 「CAFE」(いいテーマだと思った)の後に続く4文字の16進数ワードをgrep検索していたら、BABEにたどり着き、使うことにしました。当時は、それほど重要でもなければ、歴史のゴミ箱行きになる運命にあるとも思えませんでした。こうしてCAFEBABEはクラスファイル形式になり、CAFEDEADは永続オブジェクトフォーマットでした。しかし、永続オブジェクト機能は廃止され、それに伴いCAFEDEADも使用されなくなり、最終的にRMIに置き換えられました。
クラスファイルには可変サイズの項目が含まれており、埋め込まれたファイルオフセット(またはポインタ)も含まれていないため、通常は最初のバイトから末尾に向かって順番に解析されます。ファイル形式は、最下位レベルでは、いくつかの基本的なデータ型によって記述されます。
これらの基本型の一部は、コンテキストに応じて、より高水準の値(文字列や浮動小数点数など)として再解釈されます。ワードアライメントは強制されないため、パディングバイトは使用されません。クラスファイルの全体的なレイアウトは、次の表のとおりです。
| バイトオフセット | サイズ | タイプまたは値 | 説明 |
|---|---|---|---|
| 0 | 4バイト | u1 = 0xCA 16進数 | ファイルがクラスファイル形式に準拠していることを識別するために使用される マジックナンバー(CAFEBABE) |
| 1 | u1 = 0xFE 16進数 | ||
| 2 | u1 = 0xBA 16進数 | ||
| 3 | u1 = 0xBE 16進数 | ||
| 4 | 2バイト | u2 | 使用されているクラスファイル形式のマイナーバージョン番号 |
| 5 | |||
| 6 | 2バイト | u2 | 使用されているクラスファイル形式のメジャーバージョン番号。[ 3 ] Java SE 25 = 69 (16 進数 0x45)、 Java SE 24 = 68 (16 進数 0x44)、 Java SE 23 = 67 (16 進数 0x43)、 Java SE 22 = 66 (16 進数 0x42)、 Java SE 21 = 65 (16 進数 0x41)、 Java SE 20 = 64 (16 進数 0x40)、 Java SE 19 = 63 (16 進数 0x3F)、 Java SE 18 = 62 (16 進数 0x3E)、 Java SE 17 = 61 (16 進数 0x3D)、 Java SE 16 = 60 (16 進数 0x3C)、 Java SE 15 = 59 (16 進数 0x3B)、 Java SE 14 = 58 (16 進数 0x3A)、 Java SE 13 = 57 (16 進数 0x39)、 Java SE 12 = 56 (16 進数 0x38)、 Java SE 11 = 55 (0x37 hex), Java SE 10 = 54 (0x36 hex), [ 4 ] Java SE 9 = 53 (0x35 hex), [ 5 ] Java SE 8 = 52 (0x34 hex), Java SE 7 = 51 (0x33 hex), Java SE 6.0 = 50 (0x32 hex), Java SE 5.0 = 49 (0x31 hex), JDK 1.4 = 48 (0x30 hex), JDK 1.3 = 47 (0x2F hex), JDK 1.2 = 46 (0x2E hex), JDK 1.1 = 45 (0x2D hex)。以前のバージョン番号の詳細については、『The JavaTM Virtual Machine Specification 2nd edition』の脚注1を参照してください。 |
| 7 | |||
| 8 | 2バイト | u2 | 定数プールカウント。以下の定数プールテーブル内のエントリ数。このカウントは実際のエントリ数より少なくとも1つ大きい値です。以下の説明を参照してください。 |
| 9 | |||
| 10 | cpsize(変数) | テーブル | 定数プールテーブルは、可変サイズの定数プールエントリの配列で、リテラル数値、文字列、クラスまたはメソッドへの参照などの項目が含まれます。インデックスは1から始まり、合計で(定数プール数- 1)個のエントリが含まれます(注を参照)。 |
| ... | |||
| ... | |||
| ... | |||
| 10+ cpsize | 2バイト | u2 | アクセスフラグ、ビットマスク |
| 11+ cpsize | |||
| 12+ cpサイズ | 2バイト | u2 | このクラスを識別し、定数プールの「クラス」タイプのエントリへのインデックス |
| 13歳以上 | |||
| 14歳以上 | 2バイト | u2 | スーパークラスを識別し、定数プールのインデックスを「クラス」タイプのエントリに割り当てます。 |
| 15+ cpサイズ | |||
| 16歳以上 | 2バイト | u2 | インターフェース数、次のインターフェーステーブル内のエントリ数 |
| 17歳以上 | |||
| 18歳以上 | isize(変数) | テーブル | インターフェーステーブル: このクラスによって実装されたインターフェースを記述する定数プールインデックスの可変長配列 |
| ... | |||
| ... | |||
| ... | |||
| 18歳以上cpsize + isize | 2バイト | u2 | フィールド数、次のフィールドテーブル内のエントリ数 |
| 19+ cpsize + isize | |||
| 20+ cpsize + isize | fsize(変数) | テーブル | フィールドテーブル、フィールドの可変長配列 各要素は、 https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.5で定義されている field_info 構造です。 |
| ... | |||
| ... | |||
| ... | |||
| 20+ cpsize + isize + fsize | 2バイト | u2 | メソッド数、次のメソッドテーブル内のエントリ数 |
| 21+ cpsize + isize + fsize | |||
| 22+ cpsize + isize + fsize | msize(変数) | テーブル | メソッドテーブル、メソッドの可変長配列 各要素は、 https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.6で定義されている method_info 構造です。 |
| ... | |||
| ... | |||
| ... | |||
| 22+ cpsize + isize + fsize + msize | 2バイト | u2 | 属性数、次の属性テーブル内のエントリ数 |
| 23+ cpsize + issize + fsize + msize | |||
| 24+ cpsize + isize + fsize + msize | asize(変数) | テーブル | 属性テーブル、属性の可変長配列 各要素は、 https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.7で定義されている attribute_info 構造です。 |
| ... | |||
| ... | |||
| ... |
以下は、.classファイルを C スタイルの構造体として表現したものです。
構造体ClassFileFormat { u4マジックナンバー;u2マイナーバージョン; u2メジャーバージョン;u2定数プールカウント;定数プール情報[定数プールカウント- 1 ]定数プール;u2アクセスフラグ;u2 thisClass ; u2 superClass ;u2インターフェース数; u2 [インターフェース数]インターフェース数;u2 fieldsCount ; FieldInfo [ fieldsCount ]フィールド;u2 methodsCount ; MethodInfo [ methodsCount ]メソッド;u2属性数;属性情報[属性数]属性; }定数プールテーブルは、リテラル定数値のほとんどが格納される場所です。これには、あらゆる種類の数値、文字列、識別子名、クラスとメソッドへの参照、型記述子などの値が含まれます。定数プールテーブル内の特定の定数へのすべてのインデックス(参照)は、16ビット(u2型)の数値で表されます。インデックス値1はテーブル内の最初の定数を参照します(インデックス値0は無効です)。
ファイル形式の開発における歴史的な選択により、定数プールテーブル内の定数の数は、テーブルの先頭にある定数プールカウントと実際には一致しません。まず、テーブルのインデックスは0ではなく1から始まりますが、カウントは実際には最大インデックスに1を加えたものとして解釈されるべきです。[ 6 ] さらに、2種類の定数(longとdouble)がテーブル内の連続した2つのスロットを占有しますが、2番目のスロットは直接使用されることのないファントムインデックスです。
定数プール内の各項目(定数)の型は、先頭のバイトタグによって識別されます。このタグに続くバイト数とその解釈は、タグ値に依存します。有効な定数型とそのタグ値は次のとおりです。
| タグバイト | 追加バイト | 定数の説明 | 導入されたバージョン |
|---|---|---|---|
| 1 | 2+ xバイト(可変) | UTF-8 (Unicode) 文字列: 16ビットの数値 (u2型) が先頭に付いた文字列。この数値は、直後に続くエンコードされた文字列のバイト数を表します (バイト数は文字数とは異なる場合があります)。使用されるエンコーディングは実際にはUTF-8ではなく、Unicode 標準のエンコーディング形式を若干変更したものであることに注意してください。 | 1.0.2 |
| 3 | 4バイト | 整数:ビッグエンディアン形式の符号 付き32ビット2の補数 | 1.0.2 |
| 4 | 4バイト | 浮動小数点数: 32ビット単精度IEEE 754浮動小数点数 | 1.0.2 |
| 5 | 8バイト | Long: ビッグエンディアン形式の符号付き 64 ビットの 2 の補数 (定数プール テーブルで 2 つのスロットを占有) | 1.0.2 |
| 6 | 8バイト | Double: 64 ビットの倍精度 IEEE 754 浮動小数点数 (定数プール テーブルで 2 つのスロットを占有) | 1.0.2 |
| 7 | 2バイト | クラス参照: 定数プール内の完全修飾クラス名 (内部形式) (ビッグエンディアン) を含む UTF-8 文字列へのインデックス | 1.0.2 |
| 8 | 2バイト | 文字列参照: 定数プール内の UTF-8 文字列へのインデックス (ビッグエンディアンも) | 1.0.2 |
| 9 | 4バイト | フィールド参照: 定数プール内の 2 つのインデックス。最初のインデックスはクラス参照を指し、2 番目のインデックスは名前と型記述子を指します。(ビッグ エンディアン) | 1.0.2 |
| 10 | 4バイト | メソッド参照: 定数プール内の 2 つのインデックス。最初のインデックスはクラス参照を指し、2 番目のインデックスは名前と型記述子を指します。(ビッグ エンディアン) | 1.0.2 |
| 11 | 4バイト | インターフェース メソッド参照: 定数プール内の 2 つのインデックス。最初のインデックスはクラス参照を指し、2 番目のインデックスは名前と型記述子を指します。(ビッグ エンディアン) | 1.0.2 |
| 12 | 4バイト | 名前と型記述子: 定数プール内の UTF-8 文字列への 2 つのインデックス。1 つ目は名前 (識別子) を表し、2 つ目は特別にエンコードされた型記述子を表します。 | 1.0.2 |
| 15 | 3バイト | メソッドハンドル:この構造体はメソッドハンドルを表すために使用され、1バイトの型記述子とそれに続く定数プール内のインデックスで構成されます。[ 6 ] | 7 |
| 16 | 2バイト | メソッド型:この構造体はメソッド型を表すために使用され、定数プール内のインデックスで構成されます。[ 6 ] | 7 |
| 17 | 4バイト | 動的: これはブートストラップメソッドの呼び出しによって生成される動的に計算された定数を指定するために使用されます。[ 6 ] | 11 |
| 18 | 4バイト | InvokeDynamic: これはinvokedynamic命令によって使用され、ブートストラップメソッド、動的呼び出し名、呼び出しの引数と戻り値の型、およびオプションでブートストラップメソッドの静的引数と呼ばれる追加の定数のシーケンスを指定します。[ 6 ] | 7 |
| 19 | 2バイト | モジュール:モジュールを識別するために使用されます。[ 6 ] | 9 |
| 20 | 2バイト | パッケージ:モジュールによってエクスポートまたは開かれたパッケージを識別するために使用されます。[ 6 ] | 9 |
整数定数型はintegerとlongの2つだけです。高水準言語で使われるboolean、byte、shortなどの他の整数型は、整数定数として表現する必要があります。
Javaのクラス名は、完全修飾の場合、伝統的にドット区切りで表されます(例:"java.lang.Object")。ただし、低レベルのクラス参照定数内では、代わりにスラッシュを使用する内部形式が採用されています(例:"java/lang/Object")。
Unicode 文字列は、「UTF-8 文字列」という呼び名にもかかわらず、実際には Unicode 標準に従ってエンコードされているわけではありません。ただし、類似しています。違いは 2 つあります (詳細については、UTF-8C0 80を参照してください)。1 つ目は、コード ポイント U+0000 が、標準の 1 バイト エンコード ではなく、2 バイト シーケンス (16 進数)としてエンコードされること00です。2 つ目の違いは、補助文字 ( U+10000 以上のBMP外の文字) が、UTF-8 を使用して直接エンコードされるのではなく、 UTF-16に似たサロゲート ペア構造を使用してエンコードされることです。この場合、2 つのサロゲートはそれぞれ UTF-8 で別々にエンコードされます。たとえば、U+1D11E は、ED A0 B4 ED B4 9E正しい 4 バイトの UTF-8 エンコード ではなく、 6 バイト シーケンス としてエンコードされますF0 9D 84 9E。