Javaの静的メモリはどうですか?
-
03-07-2019 - |
質問
この質問は、特にJava言語に関するものです。私は、すべての静的コードに対してメモリの静的な割り当てが確保されていることを理解しています。
私の静的メモリはどのように満たされますか?インポート時に、または最初の参照時に静的オブジェクトが静的メモリに配置されますか?また、他のすべてのオブジェクトと同じガベージコレクションルールが静的オブジェクトに適用されますか?
public class Example{
public static SomeObject someO = new SomeObject();
}
/********************************/
// Is the static object put into static memory at this point?
import somepackage.Example;
public class MainApp{
public static void main( Sting args[] ){
// Or is the static object put into memory at first reference?
Example.someO.someMethod();
// Do the same garbage collection rules apply to a
// static object as they do all others?
Example.someO = null;
System.gc();
}
}
解決
インポートは、コンパイルされたコード内の命令とは相関しません。コンパイル時にのみ使用するエイリアスを確立します。
クラスをロードすることはできますが、まだ初期化されていないいくつかのリフレクトメソッドがありますが、ほとんどの場合、クラスが参照されるたびに初期化されていると想定できます。
静的メンバー初期化子と静的ブロックは、すべてソースコード順に1つの静的初期化子ブロックであるかのように実行されます。
静的メンバー変数を介して参照されるオブジェクトは、クラスがアンロードされるまで強く参照されます。通常の ClassLoader
はクラスをアンロードしませんが、アプリケーションサーバーで使用されるクラスは適切な条件下でアンロードします。ただし、これは扱いにくい領域であり、多くの診断困難なメモリリークの原因となっていますが、グローバル変数を使用しないもう1つの理由です。
(接線)ボーナスとして、考慮すべき難しい質問があります:
public class Foo {
private static Foo instance = new Foo();
private static final int DELTA = 6;
private static int BASE = 7;
private int x;
private Foo() {
x = BASE + DELTA;
}
public static void main(String... argv) {
System.out.println(Foo.instance.x);
}
}
このコードは何を印刷しますか?試してみると、「6」と印刷されていることがわかります。ここにはいくつかの作業があり、その1つは静的初期化の順序です。コードは次のように記述されているかのように実行されます。
public class Foo {
private static Foo instance;
private static final int DELTA = 6;
private static int BASE;
static {
instance = null;
BASE = 0;
instance = new Foo(); /* BASE is 0 when instance.x is computed. */
BASE = 7;
}
private int x;
private Foo() {
x = BASE + 6; /* "6" is inlined, because it's a constant. */
}
}
他のヒント
通常、「静的」というものはありません。メモリ。ほとんどのvmには永続的な世代のヒープがあり(クラスがロードされる)、通常はガベージコレクションされません。
静的オブジェクトは、他のオブジェクトと同様に割り当てられます。しかし、それらが長生きする場合、ガベージコレクターの異なる世代間で移動されます。しかし、permgenspaceにはなりません。
クラスがこのオブジェクトを永続的に保持している場合、vmが終了したときにのみ解放されます。
この静的変数 some0 は、コードでクラスが参照されるとすぐに初期化されます。あなたの例では、これはメインメソッドの最初の行で実行されます。
静的初期化ブロックを作成することでこれを検証できます。この初期化ブロックにブレークポイントを設定すると、いつ呼び出されるかがわかります。またはもっと簡単に... SomeObjectのコンストラクターにブレークポイントを設定します。
静的変数の初期化については、セクション 2.11 suns JVM仕様の静的初期化子。仕様ではガベージコレクションの実装は定義されていませんが、静的オブジェクトのガベージコレクションルールはVMによって異なると思います。
PermGenSpace (静的なものが保存される領域の適切な名前)。
したがって、ポインターによって参照されるオブジェクトは、他のオブジェクトと同様に通常のヒープに配置されます。
別のオブジェクトを参照するように静的フィールドが変更された場合、静的フィールドが指す元のオブジェクトは、他のオブジェクトと同様にGCに適格です。
クラス自体がアンロードされ、オブジェクトグラフ全体がヒープから切り取られた場合、(nullでなくても)解放することもできます。もちろん、クラスをアンロードできる場合は、他の多くの質問に適したトピックです...:)