0. 들어가며
지난 글에서 JVM의 Runtime Data Area(런타임 데이터 영역)에 대해 알아보았습니다.
이번 글에서는 Java의 Heap영역에서 참조되지 않는 객체들의 메모리를 할당 해제하는 Garbage Collectoion에 대해 자세히 살펴보겠습니다.
1.JVM 구성요소
3.Runtime Data Area 완전분석: 메모리 구조와 각 영역 역할
4. Execution Engine 완전정복: 인터프리터와 JIT의 비밀(예정)
GC의 기본 작동 원리와 JVM에 탑재된 주요 GC(예: Minor GC, Major GC), 그리고 GC 과정에서 가장 중요한 개념인 Reachability와 Stop-the-World에 대해 다루겠습니다.
1. GC란?
GC(Garbage Collection)는 프로그램 실행 중 더 이상 사용되지 않는 객체를 자동으로 탐지하고 메모리에서 회수하는 메커니즘입니다. Java는 C언어와 달리 개발자가 메모리를 직접 해제하지 않는 자동 메모리 관리(Automatic Memory Management) 방식을 채택하고 있으며 그 것을 해주는 것이 바로 GC입니다.
Java 애플리케이션은 힙(Heap) 영역에 객체를 계속 생성하며 실행됩니다.
하지만 애플리케이션이 필요로 했던 객체라도 시간이 지나면 더는 참조되지 않고 메모리에 방치되면 메모리 누수(Memory Leak)가 발생하거나 OutOfMemoryError로 애플리케이션이 중단될 수 있습니다.
C/C++처럼 수동으로 free()를 호출해 메모리를 해제하는 방식은 높은 자유도를 제공하지만 해제 시점을 잘못 처리하면 이중 해제(double free) dangling pointer 메모리 누수 같은 치명적인 오류가 발생할 수 있습니다.
자바의 GC는 이러한 문제를 자동화하여 안전하고 안정적인 메모리 관리를 도와줍니다.
GC의 주요 역할은 다음과 같습니다.
- Reachability 분석을 통해 더 이상 사용되지 않는 객체 식별
루트(Root) 객체에서 시작해 참조 그래프를 따라갈 수 없는 객체를 “가비지(garbage)”로 판단합니다. - 가비지 객체의 메모리 회수
사용되지 않는 객체를 제거함으로써 힙 공간을 확보하고, 새로운 객체가 저장될 공간을 마련합니다. - 메모리 단편화(fragmentation) 최소화
일부 GC 알고리즘은 살아 있는 객체를 한쪽으로 모으고 빈 공간을 정리(compaction)해 연속된 메모리 공간을 확보하여 할당 효율을 높입니다.
쉽게 말해 GC는 자바 프로그램에서 더 이상 사용되지 않는 객체를 자동으로 정리하고 메모리를 확보하는 메커니즘이라고 할 수 있습니다.
2. 힙 영역과 세대별(Generational) GC
자바에서 객체는 힙(Heap) 영역에 저장됩니다.
GC는 참조되지 않는 객체를 관리하기 때문에 힙 영역 구조를 이해하는 것이 중요합니다.
힙은 크게 Young Generation과 Old Generation으로 나뉩니다.

Young Generation은 새로 생성된 객체가 먼저 위치하는 Eden 영역과, Eden에서 살아남은 객체가 잠시 머무르는 Survivor 영역(S0, S1)으로 구성됩니다.
즉 Young Generation 전체는 Eden과 Survivor 영역의 합으로 이루어져 있습니다.
이 영역에서 수행되는 GC를 Minor GC라고 하며 대부분의 객체가 금방 사용되지 않게 되므로 가비지 객체를 탐지하고 회수하는 과정이 짧은 시간 내에 완료됩니다.
반면 Young Generation에서 살아남아 오래 유지되는 객체는 Old Generation(Tenured 영역)으로 이동합니다.
이 영역에서 수행되는 GC를 Major GC라고 하며 객체 수가 많고 수명이 긴 경우가 많아 Minor GC보다 시 수행 시간이 길고 애플리케이션 실행 흐름에 미치는 영향이 상대적으로 클 수 있습니다.
따라서 힙 영역은 객체의 생성과 수명에 따라 세대별로 나누어 관리되며 Young Generation에서는 빠른 Minor GC가
Old Generation에서는 상대적으로 느린 Major GC가 수행되는 구조를 가지고 있습니다.
permanent 부분은 Java 8이후부터 Metaspace로 완전히 대체되었습니다. 아래에서 자세히 다루겠습니다.
3. Reachability — GC가 객체를 ‘살려둘지 지울지’ 판단하는 기준
GC에서 객체가 Garbage인지 아닌지를 판단하는 기준이 바로 Reachability(도달성)입니다.
쉽게 말해 객체가 프로그램에서 여전히 접근 가능한 상태인지 여부를 의미합니다.
객체가 유효한 참조(valid reference)를 통해 다른 객체나 루트 객체(Root set)와 연결되어 있다면 해당 객체를 reachable 객체라고 합니다. 반대로 어떤 참조도 존재하지 않는 객체는 unreachable 객체로 판단되며 GC의 회수 대상이 됩니다.
여기서 Root set은 참조 판별의 출발점 역할을 합니다.
루트에는 스택의 로컬 변수, 정적(static) 변수, JNI(Java Native Interface) 레퍼런스 등이 포함되며 Root set에서 시작해 참조 그래프를 따라 객체가 연결되어 있는지를 확인합니다.
즉 Root set으로부터 접근 가능한 객체는 살아 있는 객체, 접근 불가능한 객체는 가비지라는 것이 GC가 객체 회수 여부를 결정하는 핵심 원리입니다.
예를 들어 힙 영역의 객체가 Java Stack, Native Stack, 또는 정적 변수 등에서 참조되고 있다면 GC는 이를 reachable로 판단하고 메모리에서 제거하지 않습니다.
반대로 어떠한 유효한 참조도 남아있지 않은 객체는 unreachable로 판단되어 가비지로 회수됩니다.
이처럼 Reachability는 GC의 핵심 판단 기준으로 객체가 살아 있는지, 회수 대상인지를 결정하는 중요한 요소가 됩니다.
아래 그림을 보며 더 자세히 알아보겠습니다.

그림을 보면 힙영역에는 파란색 오브젝트들과 빨간색 오브젝트들이 있습니다.
파란색 오브젝트는 자바 스택영역에서 참조하는 것처럼 root set으로부터 참조가 있습니다.
하지만 빨간색 오브젝트는 다른 객체를 참조할뿐 자기 자신을 참조하는 유효한 참조가 없는 것을 확인할 수 있습니다.
이 상황에서 가비지 컬렉션이 동작하게 되면 빨간색 오브젝트는 가비지로 판명되어 메모리에서 정리가 될 것입니다.
4. Stop-the-World(STW) — GC 동안 모든 애플리케이션 스레드가 멈추는 순간
Stop-the-World(STW)는 GC가 수행되는 동안 JVM이 애플리케이션 실행을 잠시 멈추는 현상을 의미합니다.
STW가 발생하면 GC 스레드를 제외한 나머지 모든 스레드는 작업을 중단하고 대기 상태에 들어갑니다.
어떠한 최신 GC 알고리즘도 구조상 완전히 STW를 피할 수는 없습니다. 다만 GC 튜닝 기법을 활용하면 Stop-the-World 시간을 최소화하여 애플리케이션의 반응성과 성능을 개선할 수 있습니다.
아래 그림과 함께 보면, STW가 발생할 때 애플리케이션 스레드가 일시 정지하고 GC가 객체를 회수하는 과정을 쉽게 이해할 수 있습니다.

애플리케이션 스레드는 여러 작업을 동시에 진행하므로 여러 스레드가 병렬로 실행됩니다. 하지만 가비지 컬렉션이 발생하면 Stop-the-World(STW) 현상이 발생하여 중간에 모든 스레드가 반드시 멈추게 됩니다.
이때 빨간색 화살표로 표시된 부분이 바로 STW 구간입니다.
GC 스레드를 제외한 나머지 모든 스레드는 GC가 끝날 때까지 대기 상태에 있다가 회수가 완료되면 다시 작업을 수행하게 됩니다.
지금까지 GC에서 중요한 두 가지 개념 Reachability(리처빌리티)와 Stop-the-World(STW)를 살펴보았습니다.
이어서 GC가 실제로 동작할 때 적용되는 두 가지 기본 알고리즘에 대해 자세히 알아보겠습니다.
5. C 기본 알고리즘 — 객체를 찾아내고 제거하는 두 가지 핵심 처리 방식
5-1 Mark & Sweep 알고리즘
먼저 첫 번째로 Mark & Sweep 알고리즘입니다.
Mark & Sweep은 가비지 컬렉션에서 가장 기본이 되는 알고리즘으로, 객체의 살아 있음 여부를 확인하고 사용되지 않는 객체를 힙에서 제거하는 방식으로 동작합니다.
이 알고리즘은 크게 두 단계로 나뉩니다.
- Mark Phase: Root set으로부터 참조된 객체를 표시(마킹)하는 단계
- Sweep Phase: 마크되지 않은 객체를 메모리에서 해제하는 단계

먼저 Mark Phase에서는 Root set에서 시작하여 참조 가능한 모든 객체를 따라가며 살아 있는 객체를 표시합니다.
여기서 Root set은 스택의 로컬 변수 정적 변수, JNI 레퍼런스 등 프로그램에서 직접 접근할 수 있는 참조들을 포함합니다.
Mark 단계에서는 객체를 제거하지 않고 단순히 “이 객체는 살아 있다”라고 표시만 합니다.
이 과정을 통해 GC는 어떤 객체가 살아 있고, 어떤 객체가 가비지인지 판단할 수 있습니다.
그다음 단계인 Sweep Phase에서는 Mark 단계에서 표시되지 않은, 즉 참조되지 않는 객체를 힙에서 제거합니다.
객체를 제거하면서 메모리를 회수하지만 객체가 있던 메모리 공간은 그대로 남기 때문에 조각(Fragmentation)이 발생할 수 있습니다.
이런 단편화 때문에 이후 새 객체를 연속된 공간에 할당하기 어려워지는 문제가 생길 수 있습니다.
예를 들어 아래 코드를 살펴보겠습니다.
void example() {
MyClass obj1 = new MyClass();
MyClass obj2 = new MyClass();
obj1 = null; // obj1은 더 이상 참조되지 않음
}
이 코드에서 obj1과 obj2는 각각 힙에 객체로 생성됩니다.
그러나 obj1에 대한 참조를 null로 설정하면 더 이상 Root set에서 접근할 수 없는 상태가 됩니다.
GC가 수행될 때 Mark 단계에서 살아 있는 객체가 표시되고 Sweep 단계에서 obj1 객체는 제거되지만, 여전히 참조가 남아 있는 obj2 객체는 힙에 남아 살아 있게 됩니다.
Mark & Sweep 알고리즘은 구조가 단순하고 가비지 객체를 안전하게 제거할 수 있다는 장점이 있지만, 메모리 단편화 문제 때문에 현대 JVM에서는 단편화를 해결한 Mark & Compact 알고리즘이나, 세대별 GC 전략과 함께 사용되는 경우가 많습니다.
5-2 Mark & Compact 알고리즘
Mark & Compact 알고리즘은 Mark & Sweep에서 발생하는 메모리 단편화(Fragmentation) 문제를 해결하기 위해 고안된 GC 알고리즘입니다.
기본 구조는 Mark & Sweep과 동일하게 Mark Phase와 Sweep Phase를 포함하지만, 추가로 Compact Phase가 존재합니다.
- Mark Phase
Root set에서부터 참조 가능한 객체를 따라가며, 살아 있는 객체를 표시합니다. 이 단계에서는 객체가 가비지인지 아닌지를 판단하는 역할만 수행하며 메모리를 실제로 해제하지는 않습니다. - Sweep Phase
Mark Phase에서 표시되지 않은 객체, 즉 더 이상 참조되지 않는 가비지 객체를 힙에서 제거합니다. 이 과정에서 메모리가 회수되지만, 단순히 제거만 하므로 객체가 있던 공간은 그대로 남아 **조각(Fragmentation)**이 발생할 수 있습니다. - Compact Phase
Sweep Phase 이후 남아 있는 살아 있는 객체들을 한쪽으로 이동시켜 연속된 메모리 공간을 확보하는 단계입니다.- 살아 있는 객체를 힙의 한쪽 끝으로 이동
- 빈 공간을 정리하여 연속된 공간 확보
- 이후 새 객체를 효율적으로 할당할 수 있음
이 과정을 통해 Mark & Compact는 메모리 단편화 문제를 최소화하고 큰 객체나 연속된 객체를 효율적으로 할당할 수 있도록 합니다.
다만 Compact Phase에서 객체 이동이 발생하므로 객체의 주소가 변경되고, 이에 따라 모든 참조자(Reference)를 업데이트해야 하는 추가 비용이 발생합니다.
따라서 Mark & Compact는 단편화 문제를 해결하는 대신 GC 수행 시간이 다소 길어질 수 있음을 염두에 두어야 합니다.
6. 발생 시점에 따른 GC — Young/Old 영역에서 언제, 어떻게 GC가 일어나는가
GC는 발생 시점에 따라 Minor GC와 Major GC로 나눌 수 있습니다.
6-1 Minor GC
Minor GC는 Young Generation 영역에서 발생하는 가비지 컬렉션입니다.
Eden 영역이 가득 차면 더 이상 새로운 객체를 생성할 수 없게 되므로 JVM은 Minor GC를 수행하여 사용되지 않는 객체를 힙에서 제거합니다. 그리고 Eden 영역에 남아 있는 객체 중 살아 있는 객체들은 Survivor 영역으로 이동하게 됩니다.
이때 두 개의 Survivor 영역 중 한 영역은 항상 비어 있어야 하며 Eden에서 살아남은 객체는 비어 있는 Survivor 영역으로 복사됩니다.
한편 Survivor 영역에서 일정 기간 동안 살아남은 객체는 Tenured 영역(Old Generation)으로 옮겨집니다.
이 과정을 프로모션(Promotion)이라고 하며, 오랜 시간 살아남는 객체를 Old Generation으로 이동시켜 효율적으로 관리합니다.
Minor GC는 대부분의 객체가 짧은 시간 내에 가비지로 판정되기 때문에 비교적 빠르게 수행되며 Old Generation보다 GC 비용이 낮습니다
그림을 통해 Minor GC 과정을 좀 더 자세히 살펴보겠습니다.

먼저, 회색 원은 유효한 참조가 없는 객체, 노란 원은 유효한 참조가 있는 객체를 나타냅니다.
Eden 영역이 가득 차면 Minor GC가 발생하며 이때 Young Generation 영역 전체에 대해 가비지 컬렉션이 수행됩니다.
먼저 Survivor 0(S0) 영역은 이미 가득 차 있었기 때문에 정리 과정에서 살아 있는 객체들은 Survivor 1(S1) 영역으로 이동합니다. 마찬가지로 Eden 영역에서도 살아 있는 객체들은 Survivor 1 영역으로 복사됩니다.
반대로 더 이상 참조되지 않는 객체, 즉 가비지로 판정된 객체는 메모리에서 해제되어 힙 공간이 확보됩니다.
그림에서 보시면 S0 영역에서 오래 살아남은 객체들은 Tenured 영역(Old Generation)으로 이동하게 되는데
이 과정을 프로모션(Promotion)이라고 부릅니다.Minor GC는 대부분 짧은 시간 내에 많은 객체를 정리하므로 비교적 빠르게 수행되며 Old Generation에 비해 GC 비용이 낮다는 특징이 있습니다.
6-2 Major GC와 Old Generation
Minor GC가 Young Generation에서 주로 발생하는 반면 Major GC는 Old Generation, 즉 Tenured 영역에서 수행되는 가비지 컬렉션입니다. Old Generation에는 Young Generation에서 살아남아 프로모션 된 오래된 객체들이 쌓이게 되므로 GC 대상 객체 수가 많고 객체의 수명도 길다는 특징이 있습니다.
Major GC가 발생하면 JVM은 Old Generation 전체를 대상으로 가비지 컬렉션을 수행합니다.
이 과정에서는 살아 있는 객체를 식별하고 더 이상 참조되지 않는 객체를 메모리에서 제거합니다.
Old Generation의 객체들은 대부분 장기적으로 유지되기 때문에 GC가 수행되는 시간도 Minor GC보다 길어질 수 있으며
이로 인해 애플리케이션의 일시적인 지연이 발생할 수 있습니다.
Minor GC와 Major GC의 가장 큰 차이점은 GC 발생 대상 영역과 처리 시간입니다.
Minor GC는 Eden 영역에서 대부분의 객체가 짧은 시간 내에 제거되기 때문에 빠르게 수행되지만, Major GC는 Old Generation 전체를 검사하고 정리해야 하므로 비교적 시간이 오래 걸립니다.
메이저 GC가 오래 걸리면 애플리케이션 스레드가 일시 정지되는 Stop-the-World 현상이 발생할 수 있으며
이로 인해 애플리케이션의 응답성이나 성능에 치명적인 영향을 줄 수 있습니다.
따라서 서버 환경에서는 메이저 GC 발생을 최소화하고, GC 튜닝을 통해 지연 시간을 관리하는 것이 매우 중요합니다.
Old Generation에서도 Mark & Sweep이나 Mark & Compact 알고리즘이 적용되며 단편화 문제를 줄이기 위해 Mark & Compact 방식이 주로 사용됩니다.
살아 있는 객체를 한쪽으로 모으고 빈 공간을 정리함으로써, 새로운 객체가 할당될 연속된 메모리 공간을 확보할 수 있습니다.
정리하자면 Minor GC는 짧은 수명을 가진 객체들을 빠르게 정리하는 역할을 하고 Major GC는 장기적으로 살아남는 객체들을 관리하며 메모리 단편화를 최소화하는 역할을 수행합니다. 이 두 가지 GC가 함께 동작하면서 자바 힙 메모리는 효율적이고 안정적으로 관리됩니다.
7. 여러 가지 GC 방식 — JVM이 메모리를 수집하는 서로 다른 전략들
GC를 진행하는 방식은 크게 시리얼 GC, 병렬(Parallel) GC, CMS GC, G1 GC의 네 가지로 나눌 수 있습니다. 각 방식이 어떤 특징을 가지고 있고, 어떤 환경에서 사용되는지 하나씩 살펴보겠습니다.
7-1 Serial GC (시리얼 GC)
Serial GC는 주로 32비트 JVM 환경에서 동작하는 싱글 스레드 애플리케이션에 적합합니다.
Minor GC와 Major GC 모두 단일 스레드로 동작하기 때문에 GC 처리 속도가 느리고, Stop-the-World(STW)가 발생하면 모든 애플리케이션 스레드가 일시 정지하게 됩니다. 따라서 동시성이 높은 서버 환경보다는 단일 스레드 환경에서 주로 사용됩니다.
7-2 Parallel GC (병렬 GC)
Parallel GC는 Serial GC의 성능을 개선하기 위해 등장했습니다. Minor GC와 Major GC 시 멀티 스레드로 동작하여 처리 속도를 높일 수 있습니다.
하지만 STW가 발생하면 여전히 애플리케이션 스레드가 모두 일시 정지하게 되므로, 처리 속도는 향상되지만 STW 문제는 여전히 존재합니다.
7-3 CMS GC (Concurrent Mark-Sweep GC)
CMS GC는 STW 시간을 최소화하기 위해 Parallel GC를 개선한 알고리즘입니다. GC는 크게 네 단계로 진행됩니다.
- Initial Mark 단계: Root set이 참조하는 객체를 마킹하며, 소규모 STW가 발생합니다.
- Concurrent Mark 단계: 애플리케이션 스레드와 동시에 살아 있는 객체를 따라 마킹합니다. 이 과정에서는 STW가 발생하지 않습니다.
- Remark 단계: Concurrent Mark 동안 변경된 객체를 다시 마킹하며, 소규모 STW가 발생합니다.
- Sweep 단계: 참조되지 않는 객체를 제거하여 메모리를 회수합니다.
CMS GC는 STW 시간을 줄여 애플리케이션의 응답성을 개선할 수 있지만, 메모리 단편화가 발생할 수 있으며, Full GC가 필요할 때는 여전히 STW가 발생합니다.
7-4 G1 GC (Garbage First GC)
G1 GC는 CMS GC를 개선한 최신 GC 알고리즘으로, JDK 8 이후 기본 GC로 지정되었습니다.
G1 GC는 현재 사용되는 GC 중 Stop-the-World(STW) 시간을 가장 짧게 유지할 수 있는 알고리즘으로 알려져 있습니다.
힙 영역을 Region 단위로 나누어 관리하며 GC가 발생할 때 전체 힙을 탐색하지 않고 필요한 Region만 선택하여 가비지를 수집합니다.
이 구조 덕분에 G1 GC는 STW 시간을 최소화하면서도 안정적인 성능을 제공합니다. 특히 대용량 힙과 멀티코어 환경에서 효율적으로 동작하며 Minor GC와 Major GC를 Region 단위로 유연하게 수행할 수 있다는 장점이 있습니다.
여기에 더해, G1 GC는 Region별 메모리 비중을 동적으로 조절할 수 있습니다. 예를 들어, 웹 서버처럼 특정 시점에 짧은 수명 객체가 급격히 생성되는 환경에서는 Young Generation 영역의 Region을 늘려 Minor GC를 효율적으로 수행합니다. 반대로 장기적으로 유지되는 객체가 많아 Old Generation 영역의 필요성이 커지면, JVM이 자동으로 Old Generation 영역의 Region 비중을 확대하여 Major GC의 부담을 줄입니다. 이렇게 Region의 비중을 상황에 맞게 조절함으로써, 전체 힙을 불필요하게 스캔하지 않고도 GC를 효율적으로 수행할 수 있으며, 애플리케이션의 응답성과 안정성을 동시에 확보할 수 있습니다.
8. PermGen을 대체한 Metaspace — JVM의 새로운 클래스 메타데이터 저장 영역
Java 8부터 JVM에는 Metaspace(메타스페이스) 라는 영역이 도입되었습니다.
이는 기존의 PermGen(Permanent Generation, 영구 영역)을 완전히 대체한 개념으로 JVM이 클래스 메타데이터를 저장하기 위해 사용하는 공간입니다.
여기서 중요한 점은 메타스페이스가 Java Heap 내부가 아니라 운영체제의 네이티브 메모리(native memory) 를 사용한다는 것입니다
즉, 힙 크기 조정과는 독립적으로 동작한다는 특징이 있습니다.
8-1 PermGen → Metaspace로 바뀐 이유
Java 7까지 JVM에는 PermGen(Permanent Generation) 이라는 영역이 존재했습니다. 이 공간은 클래스 메타데이터를 저장하기 위해 사용되었지만 힙 내부의 고정 크기 영역이라는 구조적 한계를 가지고 있었습니다. 애플리케이션의 규모와 동작 방식에 따라 필요한 메타데이터의 양은 계속 달라지는데 PermGen은 이러한 변화를 유연하게 대응하지 못했습니다. 크기가 고정되어 있기 때문에 설정이 조금만 잘못되어도 쉽게 부족해졌고 실제로 많은 개발자들이 java.lang.OutOfMemoryError: Metaspace 오류를 자주 경험하곤 했습니다.
특히 Spring, Hibernate처럼 동적으로 클래스를 생성하는 프레임워크를 사용할 경우 PermGen 공간은 더 빠르게 고갈되었습니다. 여러 개의 ClassLoader가 생성·해제되는 상황에서도 PermGen은 그 요구를 제대로 지원하지 못했습니다. 이러한 문제들을 해결하기 위해 Java 8에서는 PermGen이 완전히 제거되고 대신 Metaspace라는 새로운 구조가 도입되었습니다.
Metaspace의 가장 큰 변화는 힙이 아닌 네이티브 메모리(native memory)를 사용한다는 점입니다.
이로 인해 PermGen처럼 작은 고정 영역에 갇히지 않고, 필요에 따라 공간을 자동으로 확장할 수 있게 되었습니다. 또한 운영 환경에서는 -XX:MaxMetaspaceSize 옵션을 통해 Metaspace의 상한을 설정할 수 있어 메모리 사용량을 보다 안정적으로 제어할 수 있습니다.
클래스 로딩이 많은 현대 Java 애플리케이션 환경을 고려하면 Metaspace의 도입은 매우 자연스러운 진화였습니다. 물론 네이티브 메모리가 무한한 자원은 아니기 때문에 상한이나 운영체제 제한에 도달하면
java.lang.OutOfMemoryError: Metaspace 오류가 발생할 수 있습니다.
하지만 이는 주로 잘못된 ClassLoader 관리나 클래스 언로드 실패로 인해 나타나는 문제이며 JVM 자체의 구조적 한계 때문은 아닙니다.
정리하면 Metaspace는 PermGen의 한계를 극복하고 현대적인 Java 애플리케이션 환경을 안정적으로 지원하기 위해 도입된, 더 유연하고 확장 가능한 메모리 구조라고 할 수 있습니다.
8-2 Metaspace에는 어떤 정보가 저장될까?
클래스가 JVM에 로드되면 해당 클래스의 구조를 정의하기 위한 다양한 메타데이터가 메타스페이스에 저장됩니다.
여기에는 클래스와 인터페이스의 전체 구조 정보(필드, 메서드, 접근 제어자 등)부터 시작해서 프로그램 실행 중에 필요한 여러 메타 정보들이 포함됩니다.
예를 들어 각 클래스가 가진 런타임 상수 풀(Constant Pool) 이 메타스페이스에 저장되며 메서드와 필드의 시그니처나 어노테이션 같은 구조적 정보도 모두 이 공간에 기록됩니다.
또한 리플렉션 기능을 사용할 때 동적으로 생성되는 여러 내부 구조체들도 메타스페이스에 위치하게 됩니다. 클래스 로더와 연결된 메타데이터 역시 이 영역에 함께 저장되며, JVM이 클래스의 생명주기를 관리하는 데 중요한 역할을 합니다.
이처럼 메타스페이스는 클래스의 로딩과 실행을 위해 필요한 다양한 정보들이 모여 있는 공간이라고 보시면 됩니다.
마치며
이번 글에서는 JVM의 Garbage Collector가 어떻게 동작하고, 어떤 기준으로 메모리를 회수하는지 핵심 내용을 정리해 보았습니다.
막연히 “GC가 알아서 치운다”라고만 알고 있던 부분이 실제로 어떤 구조와 알고리즘을 통해 이루어지는지 이해하니 JVM 동작 방식이 훨씬 명확해지는 느낌이었습니다.
이번 내용을 통해 GC의 개념이 조금 더 선명하게 그려졌다면 좋겠습니다.
다음 글에서는 Execution Engine에 대해서 다루어 보겠습니다.
읽어 주셔서 감사합니다.
그럼, 다음 글에서 또 만나요!
'자바' 카테고리의 다른 글
| Object가 왜 최상위 부모인지 이제는 알고 쓰자 (0) | 2025.12.03 |
|---|---|
| [JVM 완전정복 #5] Execution Engine 완전정복: 인터프리터와 JIT의 비밀 (0) | 2025.11.17 |
| [JVM 완전정복 #3] Runtime Data Area 완전분석: 메모리 구조와 각 영역 역할 (0) | 2025.11.17 |
| [JVM 완전정복 #2] 자바 프로그램의 첫 관문 - 클래스로더 (0) | 2025.11.17 |
| JIT 컴파일러와 일반 컴파일러의 차이를 알아보자 (0) | 2025.11.14 |