Garbage Collection
가비지 컬렉션
사용하지 않는 메모리를 자동 해제. Java, Python, Go에서 지원.
가비지 컬렉션
사용하지 않는 메모리를 자동 해제. Java, Python, Go에서 지원.
Garbage Collection(GC)은 프로그램이 더 이상 사용하지 않는 메모리를 자동으로 식별하고 해제하는 메모리 관리 기법입니다. 1959년 John McCarthy가 Lisp 언어를 위해 처음 고안했으며, 오늘날 Java, Python, Go, JavaScript 등 대부분의 현대 프로그래밍 언어에서 핵심 기능으로 채택되고 있습니다.
GC의 가장 대표적인 알고리즘은 Mark-and-Sweep입니다. 이 알고리즘은 먼저 루트(전역 변수, 스택 변수 등)에서 시작하여 도달 가능한 모든 객체를 '마킹'한 뒤, 마킹되지 않은 객체들을 메모리에서 제거(Sweep)합니다. Reference Counting은 각 객체가 참조되는 횟수를 추적하여 참조 카운트가 0이 되면 즉시 해제하는 방식입니다.
현대 GC는 세대별 수집(Generational Collection) 개념을 도입하여 효율성을 높였습니다. 대부분의 객체는 생성 직후 빠르게 사라진다는 '약한 세대 가설'에 기반하여, Young Generation에서 빈번히 수집하고 Old Generation은 덜 자주 수집합니다. Java의 G1 GC, ZGC나 Go의 Concurrent GC 등이 이 원리를 활용합니다.
실무에서 GC는 개발자의 메모리 관리 부담을 크게 줄여주지만, GC 일시 정지(Stop-the-World)가 성능에 영향을 줄 수 있습니다. 특히 실시간 시스템이나 저지연이 중요한 애플리케이션에서는 GC 튜닝이 필수적이며, 최신 GC들은 일시 정지 시간을 밀리초 단위로 줄이는 데 집중하고 있습니다.
// Java - GC 동작 이해와 메모리 관리
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
public class GarbageCollectionDemo {
public static void main(String[] args) {
// 1. 기본적인 GC 대상 생성
createGarbageObjects();
// 2. WeakReference 활용 예시
demonstrateWeakReference();
// 3. 명시적 GC 요청 (권장하지 않음)
System.gc(); // JVM에 GC 요청 (실행 보장 없음)
// 4. 메모리 상태 확인
printMemoryStatus();
}
static void createGarbageObjects() {
// 이 메서드가 끝나면 list는 GC 대상이 됨
List list = new ArrayList<>();
for (int i = 0; i < 100; i++) {
list.add(new byte[1024 * 1024]); // 1MB 객체
}
System.out.println("100MB 객체 생성 완료");
// 메서드 종료 시 list 참조 소멸 -> GC 대상
}
static void demonstrateWeakReference() {
// Strong Reference: GC 대상 아님
Object strongRef = new Object();
// Weak Reference: 메모리 부족 시 GC 대상
WeakReference
"최근 API 응답 지연이 발생하는데, GC 로그를 확인해보니 Full GC가 너무 자주 발생하고 있어요. 힙 사이즈를 늘리거나 G1 GC에서 ZGC로 전환을 검토해봐야 할 것 같습니다."
"Java GC는 Young Generation과 Old Generation으로 나뉘어 동작합니다. 새로운 객체는 Eden 영역에 할당되고, Minor GC를 거쳐 Survivor 영역을 거쳐 Old Generation으로 승격됩니다. Stop-the-World 시간을 최소화하기 위해 G1 GC는 Region 단위로 병렬 수집을 수행합니다."
"이 부분에서 매 요청마다 큰 객체를 새로 생성하고 있네요. 객체 풀링을 적용하거나 재사용 가능한 버퍼를 사용하면 GC 압력을 줄일 수 있습니다. 특히 Young GC 빈도가 높아 보여요."
명시적 GC 호출은 JVM에 '힌트'일 뿐 실행을 보장하지 않습니다. 오히려 불필요한 Full GC를 유발하여 성능을 저하시킬 수 있으므로, 프로덕션 코드에서는 사용을 피하세요.
GC가 있어도 메모리 누수는 발생합니다. static 컬렉션에 객체를 계속 추가하거나, 이벤트 리스너를 해제하지 않으면 객체가 참조로 연결되어 GC 대상에서 제외됩니다.
-Xlog:gc* 옵션으로 GC 로그를 활성화하고, GCViewer나 GCEasy 같은 도구로 분석하세요. GC 빈도, 일시 정지 시간, 힙 사용 패턴을 파악하면 튜닝 포인트를 찾을 수 있습니다.