Java スタック vs ヒープ:メモリ割り当て実践ガイド
スタックメモリとは?
Java におけるスタックは、メソッド呼び出しフレーム、ローカル変数、参照変数を保持する連続したメモリ領域です。メソッドが呼び出されるたびに、新しいフレームがスタックにプッシュされます。スタックメモリは Last-In, First-Out(LIFO、後入れ先出し)方式で管理され、最も最近の呼び出しが最初にメソッド返却時に削除されます。
ヒープメモリとは?
ヒープは Java オブジェクトが作成される領域です。クラスのすべてのインスタンスと、それらを指す参照変数(オプション)を保持します。スタックとは異なり、ヒープは単一のメソッド呼び出しのライフタイムに縛られず、オブジェクトが作成されたスコープを超えて存続できます。
Java におけるメモリ割り当て
Java のメモリ割り当て とは、プログラムデータのために仮想メモリを予約するプロセスを指します。変数は宣言時に割り当てられるのではなく、参照が作成されます。new 演算子が使用されると、JVM がヒープ上にオブジェクトのための領域を確保します。
Java は効率的な管理のためにメモリを4つの異なるセクションに分割します:
- ヒープ
- スタック
- コード(バイトコード)
- スタティック(クラスデータ)
- コード セクションにはコンパイル済みバイトコードが含まれる
- スタック にはメソッドフレーム、ローカル変数、参照変数が保持される
- ヒープ にはオブジェクトと、場合によっては参照変数が格納される
- スタティック セクションには static フィールドとメソッドが保持される
ローカル変数 vs インスタンス変数
インスタンス変数 はクラス内でメソッドの外側に宣言されます:
class Student {
int num; // インスタンス変数
public void showData() {}
}
ローカル変数 はメソッド内(またはメソッドパラメータとして)で宣言されます:
public void sum(int a) {
int x = a + 3; // a と x はローカル変数
}
スタックとヒープの動作例
メソッド呼び出し時にスタックとヒープがどう相互作用するか、例で説明します。
main メソッドが m1() を呼び出すと仮定:
public void m1() {
int x = 20;
}
m1 のフレームがスタックにプッシュされ、ローカル変数 x がこのフレームに格納されます。
m1 が m2(int b) を呼び出すと、m1 のフレームの上に新しいフレームが追加されます:
public void m2(int b) {
boolean c;
}
同様に m2 が m3() を呼び出すと、さらに別のフレームが作成されます:
m3 内で Account オブジェクトがヒープ上にインスタンス化され、その参照 ref はスタック上に存在します:
class Account {
int p;
int q;
}
public void m3() {
Account ref = new Account(); // 追加コード
}
参照変数 ref は m3 のスタックフレームに作成され、ヒープ上の Account オブジェクトを指します。
m3 が完了すると、そのスタックフレームがポップされ、Account オブジェクトは参照を持たなくなり、ガベージコレクションの対象になります。
m2 と m1 でも同じプロセスが繰り返され、最終的に main に制御が戻ります。
インスタンス変数としてのオブジェクト参照
B のインスタンスへの参照を持つクラス A を考えます:
public static void main(String[] args) {
A parent = new A(); // 追加コード
}
class A {
B child = new B();
int e;
}
class B {
int c;
int d;
}
ここでは、child 参照が A オブジェクトの一部としてヒープ上に格納され、独自の B インスタンスを指しています。
重要なポイント
- 各メソッド呼び出しでスタックに新しいフレームが作成される
- ローカル変数はスタックに、インスタンス変数はヒープに存在する
- 参照変数はスタックに格納されるが、ヒープオブジェクトを指す
- スタックフレームがポップされると、そのローカル変数と参照変数は破棄され、孤立したヒープオブジェクトはガベージコレクションの対象になる
Java
- Java変数とリテラル:包括的ガイド
- Javaクラスとオブジェクト:実践ガイド
- Java抽象クラスとメソッド:包括的ガイド
- Javaネストクラスとインナークラス完全ガイド:種類、例、ベストプラクティス
- Java throw vs throws:実践例でマスターする例外ハンドリング
- Javaアノテーションタイプ完全ガイド:事前定義・カスタム・メタアノテーション
- Javaオートボクシングとアンボクシング完全ガイド:実例とベストプラクティス
- Java文字列置換マスター:replace()、replaceAll()、replaceFirst()徹底解説
- Javaメソッドオーバーライド: スーパークラスの振る舞いをカスタマイズ
- Java 10でヒープをNV-DIMMに割り当て:-XX:AllocateHeapAtオプション
- Java 8メソッド参照完全ガイド:::演算子でコードを簡潔に