가비지 컬렉션의 핵심 개념과 특징
가비지 컬렉션(Garbage Collection, GC)은 자바 가상 머신(JVM)이 프로그램에서 더 이상 사용하지 않는 메모리를 자동으로 탐지하여 해제하는 프로세스입니다. 개발자가 명시적으로 메모리를 해제할 필요가 없어 생산성이 높고 메모리 누수 위험이 줄어들지만, 제어할 수 없는 실행 타이밍과 'Stop-the-World'로 인한 성능 오버헤드라는 단점도 존재합니다.
객체의 생존 여부 판단 기준
JVM은 특정 객체가 메모리에서 제거되어야 하는지 판단하기 위해 두 가지 주요 알고리즘을 고려할 수 있습니다.
- 참조 횟수 계산(Reference Counting): 객체마다 참조 카운터를 두어 참조될 때마다 증가시키고 해제될 때 감소시킵니다. 카운트가 0이 되면 회수하지만, 두 객체가 서로를 참조하는 순환 참조 문제를 해결하지 못해 현대 JVM에서는 사용되지 않습니다.
- 도달 가능성 분석(Reachability Analysis): "GC Roots"라는 시작점에서 출발하여 참조 사슬을 따라갈 수 있는 객체를 생존한 것으로 간주합니다. 루트로부터 연결되지 않은 객체는 회수 대상으로 판단하며, 자바에서 주로 사용되는 방식입니다.
주요 GC Roots 대상:
- 로컬 변수 스택에 있는 참조 변수
- 메서드 영역의 정적(static) 필드
- JNI(Native Method Stack) 내의 참조 객체
자바의 네 가지 참조 유형
자바는 객체의 중요도에 따라 메모리 회수 시점을 조절할 수 있도록 다양한 참조 타입을 제공합니다.
- 강한 참조(Strong Reference): 일반적인
new키워드로 생성된 참조입니다. GC 대상에서 절대 제외됩니다. - 부드러운 참조(Soft Reference): 메모리가 부족한 상황에서만 회수됩니다. 캐시 구현에 유용합니다.
- 약한 참조(Weak Reference): GC가 발생하면 무조건 회수됩니다.
- 팬텀 참조(Phantom Reference): 객체 내부 데이터에 접근할 수 없으며, 객체가 메모리에서 완전히 회수되기 전 사후 처리 작업을 위해 사용됩니다.
// 소프트 참조 예시
MyBuffer source = new MyBuffer();
SoftReference<MyBuffer> softBuffer = new SoftReference<>(source);
source = null; // 이제 강한 참조가 끊겨 소프트 참조만 남음
가비지 컬렉션 알고리즘 종류
- Mark-Sweep (마크-앤-스윕): 사용 중인 객체에 마킹한 후 나머지 메모리를 지웁니다. 간단하지만 메모리 파편화 문제가 발생합니다.
- Mark-Compact (마크-앤-콤팩트): 마킹 후 생존 객체들을 한쪽으로 몰아넣어 파편화를 방지합니다. 올드 영역(Old Generation)에서 주로 쓰입니다.
- Copying (복제 알고리즘): 메모리를 두 영역으로 나누어 한쪽의 생존 객체만 다른 쪽으로 복사하고 기존 영역을 비웁니다. 속도는 빠르지만 가용 공간이 절반으로 줄어듭니다.
- Generational Collection (세대별 수집): 객체의 생존 주기에 따라 힙을 Young 영역(Eden, Survivor 0/1)과 Old 영역으로 나누어 각기 다른 알고리즘을 적용하는 최신 상용 방식입니다.
JVM 힙 메모리 구조와 GC 분류
효율적인 관리를 위해 힙은 다음과 같이 나뉩니다.
- Young Generation: 새로운 객체가 생성되는 곳이며, 여기서 발생하는 GC를 Minor GC라고 합니다.
- Old Generation: Minor GC에서 살아남은 객체들이 이동하는 곳으로, 여기서 발생하는 GC를 Major GC(또는 Full GC와 혼용)라고 합니다.
- Metaspace (Permanent): 클래스 메타데이터 등이 저장되는 영역입니다.
Full GC 발생 원인과 할당 전략
JVM은 가급적 객체를 Eden 영역에 우선 할당하지만, 다음과 같은 경우 Full GC가 발생하여 애플리케이션이 멈출 수 있습니다.
- Old 영역의 공간이 부족할 때 (큰 배열 할당 등)
- Minor GC 실행 시 Old 영역으로 승격될 객체들이 가용 공간보다 많을 때
System.gc()를 명시적으로 호출할 때 (권장되지 않음)
상용 가비지 컬렉터 분석
- Serial GC: 싱글 스레드로 동작하며 저사양 환경에 적합합니다.
- Parallel GC: Minor GC를 멀티 스레드로 처리하여 처리량(Throughput)을 높입니다. JDK 8의 기본 옵션입니다.
- CMS (Concurrent Mark Sweep): 애플리케이션 스레드와 GC 스레드가 동시에 실행되어 멈춤 현상을 줄였으나, 파편화 문제가 있습니다. (현재 사용 중단 추세)
- G1 (Garbage First) GC: 힙을 바둑판 형태의 Region으로 나누어 가비지가 많은 지역부터 우선 회수합니다. 대용량 메모리에 최적화되어 있으며 JDK 9 이후 기본값입니다.
- ZGC: 테라바이트급 메모리에서도 10ms 이하의 정지 시간을 보장하는 최신 저지연(Low Latency) 컬렉터입니다.
// ZGC 활성화 JVM 옵션
java -XX:+UseZGC -jar Application.jar