Javaでは、クラスレベルのインスタンス変数を初期化せずに宣言するのにメモリが必要ですか?
例:int i;
で初期化しない場合、i = 5;
はメモリを使用しますか?
詳細:
私は巨大なスーパークラスを持っていますが、多くの異なる(独自のスーパークラスを持つほどには違いはありません)サブクラスが拡張されています。一部のサブクラスは、スーパークラスによって宣言されたすべてのプリミティブを使用しません。このようなプリミティブを未初期化のままにして、メモリを節約するために必要なサブクラスでのみ初期化できますか?
クラスで定義されているすべてのメンバーには、明示的に初期化しない場合でもデフォルト値があるため、メモリを使用します。
たとえば、すべてのint
はデフォルトで0
に初期化され、4
バイトを占有します。
クラスのメンバーの場合:
int i;
と同じです:
int i = 0;
[〜#〜] jls [〜#〜] がインスタンス変数について言うことは次のとおりです。
クラスTにインスタンス変数であるフィールドaがある場合、新しいインスタンス変数aが作成され、デフォルト値(§4.12.5) クラスTまたはTのサブクラスであるクラスの新しく作成された各オブジェクトの一部として(§8.1.4)。インスタンス変数は、オブジェクト(§12.6)の必要なファイナライズが完了した後、フィールドであるオブジェクトが参照されなくなると、事実上存在しなくなります。
はい、値を割り当てていなくてもメモリが割り当てられます。
int i;
32 bit
メモリ(割り当て)。あなたがそれを使用しているかどうかに関係なく。
一部のサブクラスは、スーパークラスによって宣言されたすべてのプリミティブを使用しません。このようなプリミティブを未初期化のままにして、メモリを節約するために必要なサブクラスでのみ初期化できますか?
ここでも、初期化した場所に関係なく、メモリが割り当てられます。
注意する必要があるのは、未使用のプリミティブを見つけて削除することだけです。
編集:プリミティブの参照とは異なり、デフォルト値がnull
である点をもう1つ追加すると、
4 bytes(32-bit)
8 bytes on (64-bit)
元の質問はクラスレベルの変数について話していて、答えはそれらがスペースを使用するということですが、メソッドスコープの変数も見るのは興味深いです。
小さな例を見てみましょう:
public class MemTest {
public void doSomething() {
long i = 0; // Line 3
if(System.currentTimeMillis() > 0) {
i = System.currentTimeMillis();
System.out.println(i);
}
System.out.println(i);
}
}
生成されたバイトコードを見ると:
L0
LINENUMBER 3 L0
LCONST_0
LSTORE 1
さて、予想通り、コードの3行目に値を割り当てます。ここで3行目をに変更すると(そして、コンパイラエラーにより2番目のprintlnが削除されます):
long i; // Line 3
...そしてバイトコードを確認すると、3行目では何も生成されません。そのため、この時点ではメモリは使用されていません。実際、変数に代入すると、LSTOREは5行目でのみ発生します。したがって、割り当てられていないメソッド変数を宣言してもメモリは使用されず、実際にはバイトコードは生成されません。これは、最初に割り当てる場所で宣言を行うことと同じです。
はい。初期化しなくても、クラスレベルの変数はデフォルト値に割り当てられます。
この場合、int
変数は0
に割り当てられ、変数ごとに4 bytes
を占有します。
Java言語仕様もJava仮想マシン仕様も、実装の詳細であるため、これに対する答えを指定していません。実際には、 JVMS§2.7具体的には :
オブジェクトの表現
Java仮想マシンは、オブジェクトの特定の内部構造を要求しません。
理論的には、適合仮想マシンは、ビットフラグのセットを使用して多くのフィールドを持つオブジェクトを実装し、デフォルト以外の値に設定されているフィールドをマークできます。最初はフィールドは割り当てられず、フラグビットはすべて0で、オブジェクトは小さくなります。フィールドが最初に設定されると、対応するフラグビットが1に設定され、オブジェクトはそのスペースを作るためにサイズ変更されます。 [ガベージコレクターは、実行中のコードを一時的に一時停止してヒープの周りにライブオブジェクトを再配置するために必要な機構をすでに提供しています。これは、オブジェクトのサイズ変更に必要です。]
実際には、メモリを節約しても複雑で低速であるため、これは良いアイデアではありません。フィールドにアクセスするには、マルチスレッドによる破損を防ぐために、オブジェクトを一時的にロックする必要があります。次に、現在のフラグビットを読み取ります。フィールドが存在する場合は、設定されたビットをカウントして、オブジェクトのベースに対する必要なフィールドの現在のオフセットを計算します。次にフィールドを読み取ります。そして最後にオブジェクトのロックを解除します。
したがって、汎用Java仮想マシンはこのようなことを行いません。法外な数のフィールドを持つ一部のオブジェクトは、その恩恵を受ける可能性がありますが、必要とする可能性があるため、それに頼ることができませんでしたそれを行わない一般的な仮想マシンで実行します。
オブジェクトが最初にインスタンス化されるときにすべてのフィールドにスペースを割り当てるフラットレイアウトはシンプルで高速なので、それが標準です。プログラマは、オブジェクトがそのように割り当てられていると想定し、それを最大限に活用するようにプログラムを設計します。同様に、仮想マシンの設計者は、その使用を高速化するように最適化しています。
結局のところ、フィールドのフラットレイアウトは規則であり、規則ですが、とにかく信頼することができます。
Javaでは、String str;
などのクラス属性を宣言すると、オブジェクトへの参照を宣言しますが、str=value;
の値に影響を与えない限り、オブジェクトへの参照はまだ指していません。しかし、ご想像のとおり、メモリ参照を指定しなくても、参照はメモリを消費します。