😽
🧠【Java】スタックとヒープについて図解してみました。
🎯 はじめに
Javaを学び始めたとき、多くの人がつまずくのが「スタックとヒープの違い」です。
本記事では、実際のメモリ構造を図解しながら理解を進めたので、その過程を記載します。
- スタックとヒープ、それぞれ何が格納される?
- オブジェクトの実体はどこにある?
- JVMのメモリ構造はどうなっている?
🧱 JVMのメモリ構成(全体像)
Javaプログラムが実行されると、JVM(Java仮想マシン)は主記憶(RAM)上に次のような領域を確保します。
実行するjavaプログラムの例
public class MemorySample {
public static void main(String[] args) {
// Stack Frame: main に格納
User user = new User("Alice", 24);
funcA();
}
public static void funcA() {
// Stack Frame: funcA に格納
User user = new User("Alice", 24);
}
}
class User {
String name;
int age;
public User(String name, int age) {
this.name = name;
this.age = age;
}
}
主な構成要素:
領域 | 内容 |
---|---|
①Method Area | クラス定義、static変数などの共有領域 |
②Heap | newで生成されたオブジェクト(実体) |
③Stack | メソッド呼び出しごとの一時領域(Stack Frame) |
④Program Counter | 現在の命令位置(スレッドごとに存在) |
⑤Native Method Stack | JNIなどネイティブコード用 |
💡 JVMは「主記憶上」に存在する1つのプロセスであり、これらの領域もすべてRAMに展開されます。
本記事では、StackとHeapについて解説します。
🧩 スタックとヒープの違いを図解で理解
次の図を見てください。
📌 User user = new User();
を2回実行したときのメモリ構造を示しています。
public class MemorySample {
public static void main(String[] args) {
// 1回目のnew
User user1 = new User("Alice", 24);
// 2回目のnew
User user2 = new User("Alice", 24);
}
}
class User {
String name;
int age;
public User(String name, int age) {
this.name = name;
this.age = age;
}
}
✔ Stackとは?
- Stackはメソッド単位で管理される一時的な実行情報の置き場です。
- メソッド実行ごとに「Stack Frame」が積まれます。
-
ローカル変数表
に変数の値や参照が入ります。 - メソッドが終わるとそのStack Frameは破棄されます。
- ※Stackは、メソッドが呼ばれるたびに情報を積み重ねていきます。
しかし、呼び出しが多すぎるとメモリの上限を超えてしまい、StackOverflowErrorというエラーが発生します。これは、「たくさんのメソッドが終わらずに重なりすぎた状態」とイメージするとわかりやすいです。 - Stackのローカル変数表は、「スロット」と呼ばれる番号付きの格納枠によって管理されます。
public class MemorySample {
public static void main(String[] args) {
// 1回目のnew
User user1 = new User("Alice", 24);
// 2回目のnew
User user2 = new User("Alice", 24);
// プリミティブ型(値がそのままStackに格納)
int A = 1;
}
}
class User {
String name;
int age;
public User(String name, int age) {
this.name = name;
this.age = age;
}
}
✔ Heapとは?
- Heapはオブジェクトの実体を長期間保持するためのメモリ領域です。
- 複数のメソッドからもアクセス可能な、長寿命なオブジェクトの保管場所とも言えます。
-
new
で生成されたオブジェクトはすべてHeapに格納されます。 - Heapに置かれたオブジェクトは、使われなくなったタイミングで、自動的に削除される対象になります。(この削除を行ってくれるのが、「ガーベジコレクション(Garbage Collection、略してGC)」という仕組みです。)
- 同じフィールド値でも、
new
ごとに異なるインスタンス(アドレス)が生成されます。
✔ Stack → Heap の参照関係
// Stackに参照、Heapに実体
User user = new User();
user変数には、Heap上のUser@123のアドレスが入っています。
実体(name, age)はHeapにしか存在しません。
✅ つまり、スタックは参照を持ち、ヒープは実体を持つという関係です。
さらに、「参照がコピーされる」とはどういうことかを確認してみましょう。
User user1 = new User("Bob", 30);
User user2 = user1;
この場合、user1とuser2は同じHeap上のUserインスタンス(例えば User@123)を指しています。
つまり、Stack上のuser1とuser2には、同じアドレス(参照値)がコピーされているだけであり、オブジェクトの実体は1つしか存在しません。
したがって、user2.age = 35; のように変更を加えると、それは user1 が参照しているオブジェクトにも反映されます。
※ User@123 はあくまでイメージであり、実際にはJVMが割り当てるアドレスに相当するものです。
🧠 よくある誤解とその正体
誤解 | 実際の挙動 |
---|---|
user にはオブジェクトが入っている |
❌ → 実際には**参照(アドレス)**が格納されている |
new すると同じオブジェクトになる |
❌ → 毎回異なるHeap上のオブジェクトが生成される |
StackとHeapはどちらか一方しか使わない | ❌ → 両者は常にセットで使われる(Stackに参照、Heapに実体) |
📝 まとめ
- Stackは一時的なメソッド情報、Heapはnewで作られたオブジェクトの実体が置かれます。
- Stackにはアドレス(参照)が格納されます。
- GCがHeapの不要なオブジェクトを自動で回収してくれます。
- パフォーマンスやメモリ管理の観点から、StackとHeapの違いはとても重要です。
Discussion