[로봇활용_12주차] C# 세대별 가비지 컬렉션

최윤호·2025년 10월 29일
post-thumbnail

GC의 효율적인 청소 방법

C#에서 가비지 컬렉터가 매번 전체 메모리를 샅샅이 뒤진다면,
그로 인한 성능 저하(Stop-the-World)를 무시할 수 없을 거예요.
그래서 .NET의 똑똑한 가비지 컬렉터는 훨씬 더 효율적인 전략을 사용합니다.
바로 세대별 가비지 컬렉션(Generational Garbage Collection)입니다.
이 전략이 어떻게 가비지 컬렉터를 더 빠르고 스마트하게 만드는지 알아보겠습니다.

1)객체의 세대 가설

컴퓨터 과학자들이 수많은 프로그램을 분석해 보니 다음과 같은 패턴을 발견했습니다.

"대부분의 객체는 생성된 직후에 금방 쓰레기가 된다."
"오래 살아남은 객체는 앞으로도 계속 살아남을 확률이 높다."

이를 '약한 세대 가설(Weak Generational Hypothesis)'이라고 부릅니다.
세대별 가비지 컬렉션은 객체의 수명에 따라 그룹으로 나누어 관리합니다.
즉, 객체를 세대별로 분류해서 청소 효율을 높이는 전략입니다.

2)객체의 세대: 0, 1, 2

.NET의 가비지 컬렉터는 객체를 총 3개의 세대(그룹)로 나누어 관리합니다.

Generation 0 (0세대)

  • 역할: 쓰레기가 될 확률이 높은 객체가 머무는 곳.
  • 특징:
    • new키워드로 객체를 생성하면 주로 0세대에 할당됩니다.
    • 세대 중 크기가 가장 작고, 가비지 컬렉션이 가장 빈번하게 발생합니다.
    • 0세대 GC가 실행되면 대부분의 객체는 여기서 정리됩니다.
  • 비유: 사무실 책상 위의 '받은 문서함'

Generation 1 (1세대)

  • 역할: 0세대 GC에서 살아남은 객체들이 승급(Promotion)되어 오는 곳.
  • 특징:
    • 0세대보다는 GC가 덜 발생합니다.
    • 1세대 GC가 발생하면, 0세대와 1세대를 대상으로 동시에 수집이 진행됩니다.
  • 비유: 받은 문서함에서 처리되지 않고, '처리할 문서'로 분류

Generation 2 (2세대)

  • 역할: 1세대 GC에서도 살아남은, 오랫동안 사용되는 객체들이 머무는 곳.
  • 특징:
    • 가장 드물게 GC가 발생합니다.
    • 애플리케이션의 생명주기와 거의 함께하는 객체들이 주로 위치합니다.
    • 2세대 GC(Full GC라고도 부름)가 발생하면 모든 세대(0, 1, 2)를
      대상으로 수집이 진행되므로, 가장 비용이 큰 작업입니다.
  • 비유: 중요한 업무가 끝나 파일 캐비닛에 '장기 보관'되는 핵심 문서들

3)GC의 동작 흐름

1. 새 객체는 높은 확률로 0세대에 할당됩니다.
2. 0세대가 임계치에 도달하면 0세대 GC가 발생합니다.
3. 여기서 살아남은 객체들은 1세대로 승급합니다.
4. 1세대가 임계치에 도달하면 1세대 GC가 발생합니다. (0세대와 1세대 함께 청소)
5. 1세대 GC에서 살아남은 객체들은 2세대로 승급합니다.
6. 이 과정이 반복되다가 2세대마저 임계치에 도달하면, 전체 메모리를 대청소합니다.

이처럼 가비지 컬렉터는 가장 쓰레기가 많을 것으로 예상되는 0세대를 집중적으로
청소함으로써 시스템 멈춤(Stop-the-World) 시간을 최소화하고 효율은 극대화합니다.

4)코드로 확인하기

가비지 컬렉터의 동작은 우리가 직접 제어하기 어렵지만,
GC클래스를 통해 객체가 어느 세대에 속해 있는지 확인해 볼 수는 있습니다.

[코드]

class Program
{
    static void Main()
    {
        Console.WriteLine("프로그램 시작!");

        // longLivedObject는 Main 메서드가 끝날 때까지 살아있을 확률이 높습니다.
        var longLivedObject = new object();
        Console.WriteLine($"초기 세대: Gen {GC.GetGeneration(longLivedObject)}");

        // GC.Collect()를 강제로 여러 번 발생시켜 객체의 승급을 관찰해봅니다.
        // 주의! GC.Collect()는 성능에 영향을 주므로 학습 및 디버깅 목적으로만 사용하세요.
        GC.Collect(0); // Gen 0 수집
        Console.WriteLine($"Gen 0 수집 후: Gen {GC.GetGeneration(longLivedObject)}");

        GC.Collect(1); // Gen 1 수집
        Console.WriteLine($"Gen 1 수집 후: Gen {GC.GetGeneration(longLivedObject)}");

        GC.Collect(2); // Gen 2 수집 (Full GC)
        Console.WriteLine($"Gen 2 수집 후: Gen {GC.GetGeneration(longLivedObject)}");

        // 이 객체를 계속 참조하고 있으므로 프로그램 종료 시점까지 살아남습니다.
        GC.KeepAlive(longLivedObject);
    }
}

[실행 결과]

프로그램 시작!
초기 세대: Gen 0
Gen 0 수집 후: Gen 1
Gen 1 수집 후: Gen 2
Gen 2 수집 후: Gen 2

5)정리

가비지 컬렉터는 쓰레기가 나올 확률이 가장 높은 0세대 가비지 컬렉션을
집중적으로 관리함으로써 프로그램의 성능을 최대한 보장해 줍니다.

세대역할GC 빈도비유
Gen 0새로 생성된 대부분의 객체매우 높음받은 문서함
Gen 1Gen 0에서 살아남은 객체중간처리할 일 목록
Gen 2Gen 1에서 살아남은 객체매우 낮음장기 보관 캐비닛
profile
🚀 미래의 엔지니어를 꿈꾸는 훈련생의 기록 📝

0개의 댓글