C#의 Garbage Collection

break 없는 while loop·2024년 12월 29일
post-thumbnail

1. C#의 GC란?

C#의 Garbage Collection (GC)은 .NET Framework 및 .NET Core/5+에서 제공되는 메모리 관리 메커니즘으로, 더 이상 사용되지 않는 객체를 자동으로 해제하야 메모리를 회수하고 애플리케이션의 메모리 누수를 방지한다. 이는 CLR (Common Langague Runtime)에서 수행되며, 프로그래머가 메모리를 수동으로 관리할 필요를 줄여준다.

=> Java의 GC와 목표는 동일하다.

2. C# Garbage Collection의 특징

  1. 자동 메모리 관리
    • 프로그래머가 명시적으로 메모리를 해제하지 않아도 GC가 객체의 수명을 추적하고 불필요한 객체를 정리한다.
  2. 세대 기반 수집 (Generational Garbage Collection)
    • GC는 성능을 최적화하기 위해 메모리를 세 가지 세대로 나누어 관리한다.
      • Generation 0: 새롭게 생성된 객체. 짧은 수명을 가지는 객체가 주로 여기에 포함된다.
      • Generation 1: Generation 0에서 살아남은 객체.
      • Generation 2: 장시간 살아남은 객체. 상대적으로 긴 수명을 가지며 GC의 대상이 되는 빈도가 낮다.
  3. Mark-and-Sweep 알고리즘
    • GC는 다음 단계를 통해 메모리를 회수한다.
      • Mark: 사용 중인 객체와 그렇지 않은 객체를 식별
      • Sweep: 사용되지 않는 객체를 제거하고 메모리를 정리
      • Compact (선택적): 메모리 단편화를 방지하기 위해 객체를 연속적으로 배치
  4. 스레드 안정성
    • GC는 애플리케이션 실행 중에도 동작하며, 스레드 안정성을 보장한다. 하지만 높은 부하가 걸리면 GC가 멈춤 현상을 유발할 수 있다.

=> 세대 기반 수집을 통한 개선된 Mark-and-Sweep 알고리즘을 사용한다.

3. Garbage Collection 트리거 조건

  • GC는 다음 조건에서 실행될 가능성이 높다:
    • 메모리가 부족할 때
    • Generation 0의 메모리가 가득 찾을 때
    • 애플리케이션이 명시적으로 GC.Collect()를 호출했을 때
      • 하지만 이는 권장되지 않는다. GC는 자체적으로 최적화된 시점을 판단해 작동하도록 두는 것이 좋다.

4. 최적화를 위한 팁

  1. IDisposable 구현 및 using 사용
    • IDisposable 인터페이스를 구현한 객체는 Dispose 메서드를 통해 명시적으로 리소스를 해제할 수 있다.
    • using 구문을 활용하면 블록 종료 시 리소스가 자동으로 해제된다.
  2. 큰 객체 관리
    • GC는 LOH (Large Object Heap)에서 큰 객체를 관리하며, Generation 2에 속한다. 큰 객체는 가급적 최소화하고 재사용을 고려한다.
  3. 불필요한 객체 참조 해제
    • 필요 없는 객체 참조를 null로 설정하거나 범위를 벗어나게 하여 GC가 수거 대상임을 명확히 한다.

5. Java의 GC와의 비교

공통점

  1. 자동 메모리 관리
    • 두 언어 모두 개발자가 명시적으로 메모리를 해제하지 않아도 불필요한 객체를 자동으로 회수한다.
  2. Mark-and-Sweep 알고리즘
    • 기본적으로 GC는 Mark-and-Sweep 알고리즘을 기반으로 동작하며, 메모리 단편화를 방지하기 위해 일부 경우 압축(compaction)을 수행한다.
  3. Generational Garbage Collection
    • 둘 모두 객체를 세대(Generation)로 나누어 관리하며, 세대를 기반으로 최적화를 수행한다.
  4. Stop-the-World
    • GC가 실행될 때 애플리케이션의 실행을 일시 중지하는 Stop-the-World 이벤트가 발생한다.
  5. 대규모 애플리케이션에 적합
    • 두 언어의 GC는 대규모 애플리케이션에서의 메모리 관리를 위해 설계되었으며, 기본적인 GC 동작은 프로그래머가 신경 쓰지 않아도 효율적으로 작동한다.

차이점

  1. GC 구현 및 동작 방식
    • C#
      • GC는 CLR (Common Language Runtime)에서 관리한다.
      • 기본적으로 동시성 GC (Concurrent GC)백그라운드 GC를 지원하며, 애플리케이션 성능에 따라 GC 전략을 조정한다.
      • .NET Core/.Net 5+에서는 Server GCWorkstation GC 중 선택할 수 있다.
        • Server GC: 다중 스레드 환경에 최적화
        • Workstation GC: 단일 사용자 환경에 최적화
    • Java
      • GC는 JVM (Java Virtual Machine)에서 관리한다.
      • 여러 GC 옵션을 제공하며, JVM에 따라 다양한 GC 구현을 선택할 수 있다. (https://velog.io/@kys6453/Java%EC%9D%98-Garbage-Collection 참고)
      • Java의 GC는 더 많은 커스터마이징 옵션을 제공하며, JVM 실행 시 명령줄에서 GC 동작 방식을 상세히 설정할 수 있다.
  2. 메모리 구조
    • C#
      • Managed Heap을 사용하여 객체를 관리하며, 세대별로 나누어 관리한다.
      • 큰 객체는 LOH (Large Object Heap)에 별도로 저장되며, Generation 2에 포함된다.
      • 메모리 관리가 기본적으로 .NET의 런타임 내부에서 최적화되며, 프로그래머가 수동으로 개입할 일이 적다.
    • Java
      • JVM은 Young Generation, Old Generation, Metaspace로 메모리를 나눈다.
        • Young Generation: 새로운 객체 저장
        • Old Generation: 장시간 생존한 객체 저장
        • Metaspace: 클래스 메타데이터 저장 (Java 8부터 PermGen 대신 사용)
      • 각 영역에 대해 상세한 튜닝 옵션을 제공한다.
  3. 프로게이머의 개입
    • C#
      • 프로그래머가 IDisposable 인터페이스를 구현하거나 using 구문을 사용하여 명시적으로 리소스를 정리할 수 있다.
      • GC를 강제로 호출하는 GC.Collect()가 있지만 일반적으로 권장되지 않는다.
      • 기본적으로 GC 동작을 신뢰하는 방향으로 설계되었다.
    • Java
      • GC를 강제로 호출할 수 있는 System.gc()가 있지만, JVM이 GC를 최적화된 시점에 실행하도록 두는 것이 좋다.
      • Java에서는 GC 동작과 힙 크기를 JVM 옵션으로 세부적으로 조정할 수 있어, 성능 최적화를 위한 개입 여지가 더 많다.
  4. 성능과 유연성
    • C#
      • GC는 성능을 중시하며, .NET Core/5+의 Server GC와 Background GC는 다중 프로세서 환경에서 매울 효율적
      • 설정이 간단하며, 기본적으로 대부분의 상황에서 최적의 성능을 제공
    • Java
      • 다양한 GC 구현과 설정 옵션을 제공하며, 실시간 애플리케이션이나 대규모 서버 애플리케이션의 성능 요구에 따라 선택할 수 있다.
      • JVM 옵션을 통해 성능과 지연 시간에 대해 세밀한 튜닝이 가능
profile
프로그래밍 지식 아카이브용

0개의 댓글