戻る

変数

コンピュータの動作と変数

プログラムというのは、入力された情報を、処理して、応答として返すものです。ディスク上に保存されたプログラムは、実行前にメモリ上にロードされます。処理装置 (CP) は、複数のレジスタを使いながら、プログラムに書かれた命令を実行していきます。このとき、レジスタには、アドレス(メモリ番地)、命令、値が保持され、アドレス先の命令がアドレス先の値を処理した結果をアドレス先に書き戻します。

このとき、ホスト(メインフレーム)の場合は、アドレス先に命令がなければABENDS0C1となり、既に別のデータが存在していて書き戻せないときにはABENDS0C4となります。UNIX/Linux のような分散系の場合は、ストレージキーのような保護の仕組みがないので、書き戻そうとした先のデータを破壊してしまう領域違反と呼ばれるエラーとなります。分散系の場合は、破壊者ではなく、被破壊者がそのまま破壊されてしまうわけです。

つまり、コンピュータが実行可能なのはメモリ上のビット列であり、読込先/書き戻し先がなければ動かないということです。この、値を保持する仕組みが変数です。プログラムには変数が定義されており、値はその変数に保持され、メモリ上の変数から値が、オペランドスタック(演算待ち行列)やレジスタにロードされたり、処理結果がメモリ上の変数にストア(ストー)されたりするわけです。プログラムというのは、変数と、変数に対する命令の組み合わせが全てです。変数に対して命令が実行されることを、演算や処理と呼び、演算対象の値をオペランドと呼ぶことがあります。

JVM バイトコードと変数

Sun Microsystems の 『Java 仮想マシン仕様』によれば、JVM が実行可能なバイトコードのロード命令は次のようになります。

xload index
xはロードするオペランドの型: i (int), l (long), f (float), d (double), a (address)。index が指定する変数の値をロード。
xload_<n>
xはロードするオペランドの型。n は 0~3。

これらの演算対象は、ローカル変数であり、load 命令によってオペランドスタックにプッシュされます。フィールドの場合は、class ファイルフォーマットの C 的な構造体の一つ (field_info) として構築され、access_flags, name_index, descriptor_index などの値を持ち、具体的な値はdescriptor_index が指し示す constant_pool 中のエントリになります。

xload index が 2 バイトなのに対して xload_<n> は 1 バイトです。後者は前者の特殊な形態なのですが、使用する PC レジスタの個数が 2 つと 1 つでは実行速度に大きな違いがあります。通常のプログラミングでは、十分無視できる程度の差しか生みませんが、パフォーマンスがクリティカルな小さい処理の場合は、当然考慮して然るべき部分です。

xload index よりも xload_<n> を使うほうが適切なわけですが、n は 0~3 なので四つしかなく、 0 はインスタンスメソッドのオブジェクト (this キーワードの参照値) になるので、変数として利用できるのは 1~3 の三つだけです。ソースコード中で、何れのインストラクションを使うか指定するわけではなく、JVM が自動的に選択します。このことから、メソッドの中でローカル変数は 3 つまでにすることがパフォーマンス上有効だと帰結されます。

パフォーマンス特性というのは、業務やマシン、JVM のバージョンによって千変万化するものです。また、バイトコードレベルのパフォーマンスチューニングは、その単一の処理に限れば数割の向上が期待されますが、その他の全体の中での利得は微々たるものです。シリアライズや I/O ウェイトの方が大きな影響を持つのは論を待ちません。従って、パフォーマンスチューニングを目的に、ソースコードの可読性を損なうのは愚策です。パフォーマンスは、プロファイラと呼ばれるパフォーマンスモニタツールによる測定値に基づいて、実証的に、仮説を立てて検証する科学的な態度で臨むものです。

参考:The Java Virtual Machine Specification, Java仮想マシン入門Returns

プログラムと変数

変数とスコープ

Java の変数は、クラス型メンバであるフィールドと、メソッド内部で定義されるローカル変数に分けられます。フィールドは、クラスやオブジェクトが存在する限り存在し続けます。ローカル変数は、変数定義されたブロック内部でのみ有効です。シンボルの有効範囲をスコープと呼びます。

変数と初期化

Javaでは、変数は最初に型定義することで作成し、使う前には明示的に値を代入する必要があります。型という観点では、プリミティブ型と参照型に分けられます。プリミティブ型には、整数型のshort, byte, int, long, char、浮動小数点型のfloat, double、ブール代数型のbooleanが存在します。参照型には、オブジェクト型と配列が存在します。

定義場所という観点では、フィールドとローカル変数に分けられ、フィールドの場合は暗黙的に0, false, nullの何れかで初期化されますが、ローカル変数の場合は明示的に値を代入することで初めて使えるようになります。制御構造によっては、階層が異なるブロックでの初期化は、上の階層のブロックでは有効でない可能性があるので注意が必要です。一般に、型定義と同時に初期化することが安全ですが、実際に必要になるまで初期化を遅らせる「怠惰な設計」(lazy design)を採用するのが適当な場面もあります。

戻る

Copyright 2004 SUGAI, Manabu. All rights reserved.
SEO [PR] !uO z[y[WJ Cu