본문 바로가기
개발/Java

[Java] Day 9 - JVM Memory ⭐⭐⭐

by kota 2026. 9. 15.

Java 개발을 하다 보면 다음과 같은 질문을 자주 만나게 됩니다.

  • 객체는 메모리 어디에 생성될까?
  • new로 객체를 생성하면 내부적으로 어떤 일이 일어날까?
  • Stack과 Heap은 어떤 차이가 있을까?
  • user = null을 하면 객체는 바로 삭제될까?
  • Garbage Collector는 어떤 기준으로 객체를 삭제할까?
  • GC Root는 무엇일까?

이번 Day 9에서는 JVM의 메모리 구조를 이해하고, Java 객체와 Reference가 실제 메모리에서 어떤 관계를 가지는지 학습했습니다.


1. JVM Memory 구조

JVM의 메모리 구조를 개념적으로 보면 다음과 같습니다.

JVM
 ├── Heap
 ├── Stack
 ├── Metaspace
 └── PC Register

각 영역의 역할은 다음과 같습니다.

영역 주요 역할
Heap 객체와 배열이 생성되는 영역
Stack 메서드 호출, Stack Frame, 지역 변수 등을 관리
Metaspace 로딩된 클래스의 메타데이터를 관리
PC Register 현재 실행 중인 JVM 명령어 위치를 관리

이번 학습에서는 특히 Heap, Stack, Metaspace를 중심으로 살펴봤습니다.


2. Heap

Heap이란?

Heap은 Java 애플리케이션에서 객체(Object)와 배열이 생성되는 영역입니다.

예를 들어 다음 코드를 실행하면,

User user = new User("Kota");

new User("Kota")를 통해 생성된 실제 User 객체는 Heap에 생성됩니다.

개념적으로 다음과 같이 볼 수 있습니다.

Heap

┌──────────────────────┐
│ User Object          │
│                      │
│ name = "Kota"        │
└──────────────────────┘

Heap은 Garbage Collector가 객체를 관리하는 대표적인 영역입니다.


3. Stack

Stack이란?

Stack은 메서드가 호출될 때 생성되는 Stack Frame을 관리하고, 메서드의 지역 변수 등을 관리하는 영역입니다.

예를 들어:

public void test() {
    User user = new User("Kota");
}

test() 메서드가 실행되면 해당 메서드의 실행에 필요한 Stack Frame이 만들어집니다.

개념적으로 보면 다음과 같습니다.

Stack

┌──────────────────────┐
│ test()               │
│                      │
│ user ────────────────┼─────┐
└──────────────────────┘     │
                             │
                             ↓
Heap                   ┌───────────────┐
                       │ User Object   │
                       │ name = Kota   │
                       └───────────────┘

여기서 중요한 점은 user 자체가 객체는 아니라는 것입니다.

user는 Heap에 있는 User 객체를 참조하는 지역 변수입니다.


4. Object와 Reference

다음 코드를 다시 보겠습니다.

User user = new User("Kota");

이 코드는 세 부분으로 나눠서 생각할 수 있습니다.

User
↓
변수의 타입

user
↓
지역 변수

new User("Kota")
↓
Heap에 생성되는 객체

메모리 구조를 단순화하면 다음과 같습니다.

Stack

user
 │
 │ Reference
 ↓
Heap

┌──────────────────┐
│ User Object      │
│ name = "Kota"    │
└──────────────────┘

user에는 객체 자체가 저장되는 것이 아니라 Heap의 객체를 참조하기 위한 Reference가 있습니다.

따라서 면접에서는 다음과 같이 설명하는 것이 좋습니다.

지역 변수 user는 Heap에 생성된 객체를 참조하는 Reference를 가지고 있습니다.

참고로 Java 언어에서 Reference의 구체적인 구현 방식은 JVM에 의해 결정되므로, 단순히 "Stack에는 객체의 메모리 주소값이 저장된다"라고 단정하는 것은 피하는 것이 좋습니다.


5. Metaspace

Metaspace란?

Metaspace는 JVM에 로딩된 클래스의 메타데이터를 관리하는 영역입니다.

예를 들어 다음과 같은 클래스가 있다고 가정해 보겠습니다.

public class User {

    private String name;

    public User(String name) {
        this.name = name;
    }

    public void printName() {
        System.out.println(name);
    }
}

JVM이 User 클래스를 로딩하면 User라는 클래스의 구조와 필드, 메서드 등에 대한 정보를 JVM이 관리하게 됩니다.

개념적으로 보면:

Metaspace

User 클래스
├── 클래스 이름
├── 필드 정보
│   └── name
├── 메서드 정보
│   └── printName()
└── 생성자 정보

여기서 중요한 것은 클래스와 객체를 구분하는 것입니다.

User
↓
클래스 정보 → Metaspace

new User("Kota")
↓
실제 객체 → Heap

즉,

Metaspace에는 클래스에 대한 메타데이터가 있고, Heap에는 해당 클래스를 이용해 생성한 실제 객체가 있습니다.


6. PC Register

PC Register는 이번 학습에서는 깊게 다루지는 않았습니다.

쉽게 표현하면,

JVM이 현재 어떤 명령어를 실행하고 있는지 추적하기 위한 영역

이라고 이해할 수 있습니다.

Heap, Stack, Metaspace에 비해 우선순위가 낮기 때문에 이번 학습에서는 개념만 정리했습니다.


7. 실습 - User user = new User("Kota");

이번 Day 9의 핵심 실습입니다.

User user = new User("Kota");

이 코드가 실행될 때 JVM 메모리를 개념적으로 표현하면 다음과 같습니다.

                  JVM
                   │
       ┌───────────┼─────────────┐
       ↓           ↓             ↓
    Stack         Heap        Metaspace
       │           │             │
       │           │             │
    main()      User 객체       User 클래스
       │           │             │
    user ─────────→│             │
                   │             │
              name = "Kota"   클래스 메타데이터

각 영역을 정리하면:

Stack

user

현재 메서드의 지역 변수인 user가 존재합니다.

Heap

new User("Kota")

를 통해 실제 User 객체가 생성됩니다.

Metaspace

User 클래스 정보

가 JVM에 의해 관리됩니다.


8. Reference란?

Reference는 Heap에 생성된 객체를 참조하기 위한 값입니다.

예를 들어:

User user1 = new User("Kota");
User user2 = user1;

다음과 같은 구조를 생각할 수 있습니다.

Stack

user1 ───────┐
             │
user2 ───────┼────→ User Object
             │       name = "Kota"

객체는 하나지만 Reference는 두 개입니다.

따라서:

System.out.println(user1 == user2);

결과는:

true

입니다.

두 변수 모두 같은 객체를 참조하고 있기 때문입니다.


9. Garbage란?

Garbage란 프로그램에서 더 이상 접근할 수 없는 객체를 의미합니다.

예를 들어:

User user = new User("Kota");

user = null;

처음에는:

Stack

user ─────────────→ User("Kota")

하지만 user = null이 실행되면:

Stack

user = null

Heap

User("Kota")
↑
└── 더 이상 참조하는 경로가 없음

이 객체는 더 이상 프로그램에서 접근할 수 없게 됩니다.

따라서 Garbage Collection의 대상이 될 수 있습니다.


10. GC 대상이 된다고 바로 삭제되는 것은 아니다

여기서 중요한 점이 하나 있습니다.

다음 코드를 실행했다고 해서:

user = null;

객체가 즉시 삭제되는 것은 아닙니다.

정확히는:

Reference 제거
      ↓
GC Root에서 객체에 도달할 수 없음
      ↓
Garbage Collection의 대상이 될 수 있음
      ↓
GC 실행
      ↓
객체 메모리 회수

즉,

GC 대상이 되는 것과 실제로 메모리가 회수되는 것은 다른 개념입니다.


11. GC Root

GC가 객체를 회수할지 판단할 때 가장 중요한 개념이 GC Root입니다.

GC는 단순히 "Reference의 개수가 몇 개인가?"를 보는 것이 아니라 GC Root에서 객체까지 도달할 수 있는가를 기준으로 판단합니다.

예를 들어:

GC Root
   │
   ↓
Object A
   │
   ↓
Object B

GC Root에서 A와 B에 접근할 수 있으므로 해당 객체들은 살아있는 객체입니다.

반대로:

GC Root


Heap

Object C → Object D

GC Root에서 C로 이어지는 경로가 없다면 C와 D는 Garbage Collection 대상이 될 수 있습니다.


12. 대표적인 GC Root

대표적인 GC Root는 다음과 같습니다.

  • 현재 실행 중인 메서드의 지역 변수와 같은 Stack 참조
  • Static 필드
  • 실행 중인 Thread 등

중요한 것은 GC Root에서 객체까지의 참조 경로가 존재하는가입니다.


13. 순환 참조와 Garbage Collection

Java에서는 객체끼리 서로 참조하고 있어도 GC 대상이 될 수 있습니다.

예를 들어:

User user1 = new User("Kota");
User user2 = new User("Tom");

user1.friend = user2;
user2.friend = user1;

개념적으로:

User A ─────→ User B
  ↑             │
  └─────────────┘

서로 참조하고 있기 때문에 처음 보면 Garbage가 되지 않을 것처럼 보입니다.

하지만:

user1 = null;
user2 = null;

이후에는:

GC Root

    X

Heap

User A ─────→ User B
  ↑             │
  └─────────────┘

GC Root에서 A와 B까지 도달할 수 있는 경로가 없습니다.

따라서 두 객체 모두 Garbage Collection 대상이 될 수 있습니다.

즉 Java의 GC는 단순한 참조 횟수(reference counting)가 아니라 Reachability(도달 가능성)를 기준으로 객체의 생존 여부를 판단합니다.


14. Object, Reference, GC 관계 정리

지금까지 배운 내용을 하나의 그림으로 정리하면 다음과 같습니다.

Stack
┌──────────────────────┐
│ user ────────────────┼────────┐
└──────────────────────┘        │
                                │ Reference
                                ↓
Heap                     ┌───────────────┐
                         │ User Object   │
                         │ name = Kota   │
                         └───────────────┘
                                ↑
                                │
                             GC Root

Reference가 존재하고 GC Root에서 객체까지 도달할 수 있다면 객체는 살아있는 상태입니다.

반대로:

Stack

user = null


Heap

User Object
name = Kota

GC Root에서 객체에 도달할 수 없다면 해당 객체는 Garbage Collection 대상이 될 수 있습니다.


15. static은 어디에 저장될까?

Day 9에서 추가로 헷갈렸던 부분이라 함께 정리해 봤습니다.

class User {

    static int count = 10;

    String name;
}

여기서 count는 객체마다 존재하는 값이 아니라 클래스에 소속된 static 필드입니다.

따라서 다음 두 개는 구분해야 합니다.

클래스 메타데이터
→ Metaspace

static 필드의 실제 데이터
→ JVM이 관리하는 클래스 수준의 데이터

Java 8 이후 HotSpot JVM에서는 static 필드의 실제 데이터 자체를 단순히 "Metaspace에 저장된다"라고 보면 안 됩니다.

따라서 면접에서는

static은 Metaspace에 저장된다.

라고 외우기보다,

Metaspace는 클래스 메타데이터를 관리하는 영역이고, static 데이터는 클래스 수준에서 관리되는 데이터이다.

라고 이해하는 편이 정확합니다.


16. Heap과 Stack 차이

면접에서 가장 많이 질문하는 부분입니다.

Heap

  • 객체와 배열이 생성됨
  • GC의 주요 관리 대상
  • 여러 스레드가 공유하는 영역

Stack

  • 메서드 호출에 따른 Stack Frame 관리
  • 지역 변수 관리
  • 메서드 호출과 종료에 따라 Stack Frame이 생성되고 제거됨
  • 일반적으로 각 Thread마다 별도의 Stack을 가짐

예를 들어:

public void test() {

    User user = new User("Kota");

}

개념적으로:

Stack                         Heap

test() Frame
┌─────────────────┐
│ user ───────────┼────────→ User Object
└─────────────────┘            │
                               │
                          name = "Kota"

이 관계를 이해하는 것이 Day 9의 가장 중요한 부분입니다.


17. Day 9 핵심 정리

Heap
→ 객체와 배열

Stack
→ 메서드 호출, Stack Frame, 지역 변수

Metaspace
→ 클래스 메타데이터

Object
→ new를 통해 생성되는 실제 인스턴스

Reference
→ 객체를 참조하는 값

Garbage
→ GC Root에서 도달할 수 없는 객체

GC Root
→ GC가 Reachability를 판단하기 시작하는 기준점

그리고 다음 코드를 보면:

User user = new User("Kota");

이렇게 생각하면 됩니다.

User
→ 클래스 정보 → Metaspace

user
→ 지역 변수 → Stack

new User("Kota")
→ 실제 객체 → Heap

user
→ Reference를 통해 Heap의 객체를 참조

18. 면접 체크

Q1. Heap과 Stack의 차이는?

Heap은 주로 객체와 배열이 생성되고 GC가 관리하는 영역이며, Stack은 메서드 호출에 따른 Stack Frame과 지역 변수 등을 관리하는 영역입니다.

Q2. Object와 Reference의 관계는?

Object는 Heap에 생성되는 실제 인스턴스이고, Reference는 해당 객체를 참조하기 위한 값입니다.

Q3. Garbage란 무엇인가?

GC Root에서 더 이상 도달할 수 없는 객체이며 Garbage Collection의 대상이 될 수 있습니다.

Q4. GC Root란 무엇인가?

GC가 객체의 생존 여부를 판단할 때 시작점으로 사용하는 기준입니다. 대표적으로 Stack의 참조, static 필드, 실행 중인 Thread 등이 있습니다.


마무리

Day 9에서 가장 중요했던 것은 JVM 메모리 영역의 이름을 외우는 것이 아니라,

User user = new User("Kota");

이 코드 한 줄을 보고

"어떤 것이 Stack에 있고, 어떤 것이 Heap에 있으며, Reference가 어떻게 연결되고, 언제 Garbage가 되는가?"

를 설명할 수 있는 것입니다.

특히 다음 그림을 기억해 두면 좋습니다.

          Metaspace
              │
          User 클래스
              │
              ↓
Stack ── Reference ──→ Heap
 user                  User Object
                       name = "Kota"

              ↓
     GC Root에서 도달 불가능
              ↓
           Garbage
              ↓
             GC

Day 9를 통해 Java 코드의 동작을 단순히 문법으로만 보는 것이 아니라 JVM 메모리 관점에서 바라보는 기초를 익혔습니다.

반응형

'개발 > Java' 카테고리의 다른 글

[Java] Day 8 - Exception  (0) 2026.09.14
[Java] Day 7 - Stream API 정리  (0) 2026.09.12
[Java] Day 6 - Lambda + Functional Interface  (0) 2026.09.11
[Java] Day 5 - HashMap + equals/hashCode  (0) 2026.09.10
[Java] Day 4 - Collection Framework  (0) 2026.09.09

댓글