백엔드/Java

☕️ 자바 개발자의 필수 지식: JVM의 작동 원리 깊이 이해하기 - 메모리 구조와 가비지 컬렉션(GC)

hawon6691 2025. 10. 9. 12:16
728x90

자바 개발자라면 누구나 한 번쯤 "JVM"과 "GC"라는 단어를 들어봤을 겁니다. 하지만 이 두 핵심 요소가 실제로 어떻게 작동하는지 정확히 이해하는 개발자는 많지 않죠. 자바 애플리케이션의 성능과 안정성을 결정짓는 핵심 기둥인 **JVM(Java Virtual Machine)**의 메모리 구조와 **가비지 컬렉션(Garbage Collection)**의 원리를 쉽고 깊이 있게 파헤쳐 봅시다!


1. JVM, 자바의 든든한 실행 환경

JVM은 자바 바이트코드를 운영체제에 독립적으로 실행시켜주는 가상 기계입니다. 자바가 "Write Once, Run Anywhere"를 실현할 수 있게 해주는 마법 같은 존재죠. JVM이 애플리케이션 실행을 위해 사용하는 메모리 공간을 **런타임 데이터 영역(Runtime Data Area)**이라고 부르며, 이는 크게 다섯 가지 영역으로 나뉩니다.

주요 메모리 영역 (Runtime Data Area)

영역 역할 GC 대상 여부 특징
힙 (Heap) 객체와 배열이 저장되는 공간. 대부분의 동적 데이터 할당이 이루어집니다. O (GC의 주요 대상) JVM 전체에서 공유하는 영역입니다. Young/Old 영역으로 나뉘어 GC 최적화에 사용됩니다.
스택 (Stack) 메서드 호출 시 지역 변수, 매개 변수, 리턴 주소 등이 저장되는 공간. X 스레드마다 독립적입니다. 메서드 호출이 끝나면 자동으로 해제됩니다.
메서드 영역 (Method Area) 클래스 정보, 필드 정보, 메서드 정보, Static 변수, 상수 풀 등이 저장됩니다. O (클래스 메타데이터 GC 대상) JVM 전체에서 공유하는 영역입니다. Java 8 이후 Metaspace라는 네이티브 메모리 영역으로 대체되었습니다.
PC 레지스터 현재 실행 중인 JVM 명령어의 주소를 저장합니다. X 스레드마다 독립적입니다.
네이티브 메서드 스택 JNI(Java Native Interface)를 통해 네이티브(C/C++) 코드를 호출할 때 사용되는 스택입니다. X 스레드마다 독립적입니다.

📌 핵심 요약: 개발자가 가장 신경 써야 할 영역은 객체가 살아 숨 쉬고 GC가 활동하는 힙(Heap) 영역입니다.


2. 힙(Heap) 영역의 세대(Generation) 구분

JVM은 대부분의 객체가 짧은 시간 내에 소멸한다는 "Weak Generational Hypothesis(약한 세대 가설)"에 근거하여 을 **Young Generation(Young 영역)**과 **Old Generation(Old 영역)**으로 나눕니다. 이는 GC 작업을 효율적으로 만들기 위함이죠.

힙 영역의 세부 구조

영역 역할 발생 GC
Young 영역 새롭게 생성된 객체가 할당되는 공간. (Eden, Survivor 1, Survivor 2로 구성) Minor GC
Old 영역 Young 영역에서 여러 번 살아남은(오래된) 객체들이 이동하는 공간. Major GC (Full GC)

Minor GC (Young GC)의 동작 순서

  1. 새 객체는 Eden 영역에 할당됩니다.
  2. Eden 영역이 가득 차면 Minor GC가 발생합니다.
  3. GC 시점에 살아남은(참조되는) 객체들은 Survivor 1 영역으로 이동하고, **객체 나이(Age)**가 1 증가합니다.
  4. Eden과 Survivor 1 영역에서 살아남은 객체들은 Survivor 2로 이동합니다. 이때 Survivor 1 영역은 깨끗하게 비워집니다.
  5. 이 과정을 반복하며, 특정 나이(Threshold) 이상이 된 객체는 Old 영역으로 승격(Promotion)됩니다.

3. 메모리 자동 청소부: 가비지 컬렉션 (GC)

**가비지 컬렉션(GC)**은 더 이상 참조되지 않는(Unreachable) 객체를 자동으로 찾아내어 힙 메모리에서 제거하고 메모리를 회수하는 프로세스입니다. 개발자가 수동으로 메모리를 해제할 필요가 없게 해주어 메모리 누수(Memory Leak)를 방지하고 생산성을 높여줍니다.

GC 동작의 핵심 개념

1. 도달성 (Reachability)

GC의 가장 기본적인 원리입니다. 특정 객체가 프로그램의 GC Root (스택의 지역 변수, 정적 변수 등)에서부터 참조 체인을 따라 도달 가능한지 여부를 판단합니다. 도달 불가능한 객체만 가비지(쓰레기)로 간주되어 회수 대상이 됩니다.

2. Stop-The-World (STW)

GC가 동작하는 동안 GC 스레드를 제외한 모든 애플리케이션 스레드가 일시적으로 멈추는 현상을 말합니다. GC가 객체의 참조 관계를 정확히 파악하고 메모리를 정리하기 위해서는 애플리케이션의 동작을 멈춰야 합니다. GC 튜닝의 목표는 이 STW 시간을 최소화하여 사용자 경험에 미치는 영향을 줄이는 것입니다.

3. GC의 주요 알고리즘 (Mark, Sweep, Compact)

  • Mark (마킹): GC Root에서부터 시작해 참조되는 객체들을 추적하며 '살아있다'고 표시(Mark)합니다.
  • Sweep (청소): 마킹되지 않은 객체들(가비지)을 힙에서 제거하여 메모리 공간을 회수합니다.
  • Compact (압축): 청소 후 흩어진 객체들을 한곳으로 모아 메모리 단편화(Fragmentation)를 제거합니다. Old 영역 GC에서 주로 사용됩니다.

대표적인 GC 종류

JVM은 다양한 GC 알고리즘을 제공하며, 각 버전과 사용 목적에 따라 적합한 컬렉터를 선택할 수 있습니다.

GC 종류 특징 주요 목표
Serial GC 싱글 스레드로 GC 실행. STW 시간이 길어 성능이 낮음. 싱글 코어 시스템, 소규모 데이터셋
Parallel GC Young 영역 GC(Minor GC)를 병렬로 처리. STW 시간은 존재하지만 처리량(Throughput)이 높음. 높은 처리량(대용량 배치 작업 등)
CMS GC Old 영역 GC 작업의 대부분을 애플리케이션 스레드와 **동시(Concurrent)**에 수행하여 STW 시간을 최소화. 짧은 응답 시간(Latency)
G1 GC (Garbage-First) 힙을 영역(Region) 단위로 나누어 관리. 가장 효율이 좋은 영역부터 GC를 수행. Java 9+의 기본 GC. 짧고 예측 가능한 STW 시간, 대용량 힙

🚀 마무리하며: JVM 지식으로 성능 최적화하기

JVM의 메모리 구조와 GC의 작동 원리를 이해하는 것은 단순한 지식 습득을 넘어, 자바 애플리케이션의 성능 문제를 진단하고 최적화하는 데 필수적입니다. 특히 힙 메모리 사용 패턴, GC 발생 빈도와 STW 시간을 모니터링하고 적절한 GC 컬렉터(-XX:+UseGCName)를 선택하는 것은 안정적인 서비스를 운영하는 핵심 역량입니다.
이 지식을 바탕으로 여러분의 자바 코드가 더욱 효율적으로, 안정적으로 작동하길 바랍니다! ✨

728x90