😽

🧠【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