Javaクラスファイルの構造
0 363

Javaクラスファイルの構造は以下の構造体定義で示すことができます。

 ClassFile {
         u4 magic;
         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];
     } 

マジックナンバー

ClassFile構造体の先頭はマジックナンバーです.その値は16進でCAFEBABE と決まっています.先頭がCAFE BABEでないファイルはベリファイをパスしません。

バージョン

次の項目はこのクラスファイルを作成したバイトコード・コンパイラのバージョンです.バージョン番号がJavaVMのサポートしていないものだった場合は,それを実行することは許されていません.

コンスタントプール

コンスタントプールは定数やクラス名などを保持するためのものです.
コンスタントプールは可変長の要素を持つ,0~ConstantPoolCount-1までの一種の配列ですが,クラスファイルには0番目の要素は含まれていません.0 番のエントリはJavaVM内部用に予約されており,クラスファイルで使用することはできません.同時に0番は無効なインデックスであり,これを使用するクラスファイルはベリファイをパスしません.
コンスタントプールは配列として表現されてはいますが,要素が可変長であるため,添字から各エントリへ単純にアクセスすることはできません.  

アクセスフラグ

このクラスの修飾子に対応するフラグです.access_flagsの各ビットが public,final,interface,abstractの各修飾子に対応しています.finalと abstract,finalとinterfaceのように,同時にセットすることが許されていないフラグもあり,もしそれらが同時にセットされているとベリファイをパスしません.
通常の修飾子とは異なるsuperというフラグもありますが,これは古いコンパイラとの互換性を保つためのものです.新しいコンパイラでは常にセットされていなければなりません.
現在のバージョンで使われていないビットもありますが,それらは将来に備えて予約されており,現在は常に0にしなければなりません.これはフィールド情報やメソッド情報にあるアクセスフラグでも同様です.

thisクラス

this_classはこのクラス自身を表しています.これはコンスタントプールへのインデックスになっており,コンスタントプール中の該当するエントリはこのクラスへの参照を表すCONSTANT_Class_infoになっています.

スーパークラス 
super_classは直接のスーパークラスを表しています.直接のスーパークラスを持たないクラス(即ちjava.lang.Object)やインターフェースの場合は,この数値は0になります.それ以外の場合はthis_classと同様です. 

スーパーインターフェース 
interfaces[]は直接のスーパーインターフェースを表します.スーパークラスとは異なり,直接のスーパーインターフェースは複数持つ可能性があるので配列になっています.直接のスーパーインターフェースを持たない場合は interface_countが0になり,配列そのものがなくなります.それ以外は this_classやsuper_classと同様です.
図4の例では,this_classが2,super_classが1になっています.これより順に辿ると,このクラスの名前はGalaxyで,そのスーパークラスの名前は Sovereignであることが分かります. 

フィールド情報

fields[]はfield_info構造体の配列で,このクラスで定義しているフィールドに関する情報を保持しています.

access_flagsの各ビットは,このフィールドの修飾子に対応しています. publicとprivate,finalとvolatileなどを同時にセットするとベリファイをパスしません.また,このクラスファイルがインターフェースを表していた場合, finalとstaticがセットされていないとベリファイをパスしません.
name_indexで指定されるCONSTANT_Utf8_infoはフィールド名として有効な文字の並びを保持していなくてはなりません.そうでなければベリファイをパスしません.descriptor_indexについても同様です.
コンスタントプール回りの例が図4にあります.もう見れば分かると思いますが,fields[0]がint型のNcc,fields[1]がint型のMiranda,fields[2]が iveLongAndProsper型のYouWillBeAssimilatedというフィールドを,それぞれ表しています.  

メソッド情報

methods[]はmethod_info構造体の配列になっており,このクラスで定義されているメソッドに関する情報を保持しています.method_info 構造体は形式だけならばfield_info構造体と同じですが,ディスクリプタやアクセスフラグの詳細などが異なっています.

access_flagsの各ビットは,このメソッドの修飾子に対応しています. publicとprotected,abstractとfinalなど,同時に使用できないフラグもあります.それらが同時にセットされていた場合は,ベリファイをパスしません.  

属性

属性とは,今まで紹介された以外の様々な情報を保持するものです. ClassFile構造体の他,field_info構造体,method_info構造体,そして属性であるCode_attribute構造体の中で使われます.属性には仕様書で定義されている定義済み属性と,新たにユーザーが定義する属性とに分けられます.
属性の基本フォーマットとなるattribute_info構造体を図8に示します.全ての属性はこのフォーマットに従わなければなりません.逆に言えばこのフォーマットに従っており,且つ定義済み属性で予約されていない有効な名前を持つ属性ならば,新たに定義することはユーザーの自由です.

当然のことながら,ユーザーによって新たに定義された属性の中には JavaVMが認識できないものも出てきます.このため,認識できない属性を単純に読み飛ばすことはJavaVMに必須の機能になっています. 

 
定義済み属性
JavaVM仕様書で定義されている定義済み属性の一覧を下記の表に示します.このうち,JavaVMが必ず認識しなければならない属性はCode属性,ConstantValue 属性,Exceptions属性,InnerClasses属性,Synthetic 属性の5つです.それ以外の属性については必ずしも認識する必要はありません.(もちろん認識した方がより良いのですが.)
表3:定義済み属性一覧

属性内容使用される場所
ConstantValue属性staticの定数フィールドの値field_info
Code属性 バイトコードなど,メソッドの実装に関する情報method_info
Exceptions属性メソッドがスローするチェック例外method_info
InnerClasses属性内部クラスに関する情報ClassFile
Synthetic属性このメンバーがソースコードに現れないものであることを示すClassFile
field_info
method_info
SourceFile属性 このクラスを定義しているソースファイル名ClassFile
LineNumberTable属性code配列への添字(即ちバイトコード)と,その元となるソースファイル中の対応する行番号Code_attribute
LocalVariableTable属性 各メソッドのローカル変数に関する情報 Code_attribute
Deprecated属性 クラス,インターフェース,メソッド,フィールドが,既に新しいものと置き換えられたことを示すClassFile
field_info
method_info

0 363
みんなのツイート (0)

関連サマリー


  • JavaVM 0 Votes 407 閲覧数


    JavaVMとは

    JavaVM(Java仮想マシン、Java Vitrual Machine、JVM)とは、Javaプログラムを実行するためのソフトウェアのことです。

    JavaVMはWindowsやUnix、linux、MacOSなどのOSの上に動作するものですので、環境OS毎にそれぞれ異なる実装が作成されます。

    それによって、Javaプログラムは「Write once, run anywhere」(WORA、一度書けば、どこでも実行できる)という特徴があり、特にプラットフォームに依存しません。

    以下の図でそれらの階層関係を示します。


    JREとJDK

    JRE(Java Runtime Environment)とは、Javaプログラムの実行環境です。JavaVMとJavaプログラム実行に必要なライブラリが含まれています。

    JDK(Java Development Kit)とは、Javaプログラムの開発環境です。JDKにはJREとJavaプログラムの開発に必要なツールなどが含まれています。

    クラス

    Javaプログラムの基本構成単位はクラスです。

    コンパイルでネイティブ・コードを作成する C や C++ などの言語は、通常、ソース・コードをコンパイルした後、リンクを行う必要があります。このリンク処理では、ソース・ファイルを別々にコンパイルして得られたコードおよび共有のライブラリー・コードをマージして、1個の実行プログラムが作成されます。

    クラスのリンクは、別個の手順としてではなく、JVM がクラスをメモリーにロードする際の処理の一部として行われます


  • JavaVM 0 Votes 480 閲覧数


    このトピックでは、JavaVMのメモリ管理の仕組を取り上げて説明します。

    メモリ領域の構成


    JavaVMのメモリ構成はかきにようになります。

    OS固有領域Cヒープ領域
    JavaVM自身が使用する領域です。JNIで呼び出されたネイティブライブラリでも使用されます。スタック領域
    Javaスレッド毎に保持するスタックの領域です。JavaVM固有領域Permanent領域
    ロードされたclassなどの情報が格納される領域です。Javaヒープ
    JavaVM上で起動するJavaプログラムのリソースを管理する領域。New領域New領域
    新規オブジェクトと閾値(-XX:MaxTenuringThreshold)未満のオブジェクトが配置されます、Young領域とも呼ばれるます。Eden領域
    新規のオブジェクトが配置されます。From領域
    CopyGC(ScavengeGC、マイナーGC)が実行された際に、使用中のオブジェクトはここへコピーされます。To領域
    CopyGC(ScavengeGC、マイナーGC)が実行された際に、使用中のオブジェクトはここへコピーされます。Old領域
    New領域で閾値(-XX:MaxTenuringThreshold)を超えたオブジェクトが配置されます、Tenured領域とも呼ばれるます。GC種類

    Javaでは、「Scavenge GC」と「Full GC」という2種類のガベージ・コレクションが実行されます。Scavenge GCはNEW領域のみを対象とした短時間で終了するガベージ・コレクションであり、頻繁に実施されます。一方、Full GCはNEWとOLD両方の領域を対象とした大がかりなガベージ・コレクションであり、比較的低い頻度で実施されます。

    タイミング

    以下のタイミングでGCが実施されます。

    ヒープメモリ中に新規オブジェクトを作成するために必要な空き領域が足りなくなったときプログラム中でSystem.gc()が実行されたときJavaVMで実行する処理がなくなってアイドル状態になったとき

    下記オプションで定期的なGCを設定することができます。

    -Dsun.rmi.dgc.server.gcInterval
    JDK6デフォルト3600000(1時間))-Dsun.rmi.dgc.client.gcInterval
    JDK6デフォルト3600000(1時間))OutOfMemoryError

    メモリを割り当てる必要があるが、割り当てられるメモリが存在しないとき、OutOfMemoryErrorが発生します
    例として、OutOfMemoryErrorが発生するケースを取り上げます。

    New領域が溢れた場合Old領域が溢れた場合参照されつづけるオブジェクトが大量に存在する場合に溢れる。Cヒープが溢れた場合
    Javaのスレッドが大量に作成された場合に溢れます、Cヒープが溢れてOutOfMemorryErrorが発生した場合、スタックトレースの先頭が「Native Method」です。
    スレッド数はOSのパラメタで設定されており、それが大きな値で設定されている場合に発生します。