C# 가비지 컬렉터(Garbage Collector) 원리

C#의 가비지 컬렉터(Garbage Collector, GC)는 .NET 환경에서 메모리 관리의 핵심 요소 중 하나입니다.

1. 참조 기반 메모리 관리

  • .NET에서 객체는 힙에 동적으로 할당됩니다.
  • GC는 더 이상 참조되지 않는 객체를 "가비지"로 간주하고 회수합니다.
  • 객체에 대한 참조가 더 이상 존재하지 않을 때 해당 객체가 더 이상 필요하지 않다고 판단합니다.

2. 마킹(Marking)과 회수(Sweeping) 이해하기

가비지 컬렉션의 주요 원리 중 하나인 마킹과 회수에 대해 알아봅니다.

1. 마킹 (Marking)

마킹 단계는 가비지 컬렉션 프로세스의 첫 번째 단계입니다. 이 단계에서는 힙 메모리 내의 모든 객체를 순회하며 "살아 있는" 객체와 "죽은" 객체를 구분합니다.

  • "살아 있는" 객체란?
    • 아직 프로그램에서 참조되고 있는 객체를 의미합니다.
    • 이 객체는 아직 유효하게 사용 중이며, 메모리에서 제거되어서는 안 됩니다.
  • "죽은" 객체란?
    • 더 이상 프로그램에서 참조되지 않는 객체를 의미합니다.
    • 이러한 객체는 프로그램에서 사용되지 않기 때문에 안전하게 메모리에서 회수될 수 있습니다.

마킹 단계에서는 루트 객체(스택, 전역 변수 등에서 직접적으로 참조되는 객체)부터 시작하여 참조된 모든 객체를 따라가며 "살아 있는" 객체를 표시합니다.

2. 회수 (Sweeping)

회수 단계는 마킹 단계 후에 이루어집니다. 이 단계에서는 마킹되지 않은 모든 객체, 즉 "죽은" 객체를 메모리에서 제거하는 작업을 수행합니다.

  • 메모리 회수
    • 마킹되지 않은 객체의 메모리를 해제하여, 이 메모리를 다시 사용 가능한 풀(pool)에 반환합니다.
  • 메모리 조각화
    • 메모리 내의 사용 가능한 공간과 사용 중인 공간이 조각화될 수 있습니다. 일부 가비지 컬렉터는 추가적인 "압축" 단계를 포함하여 이러한 메모리 조각화를 해결하기도 합니다.

요약: 마킹 단계에서는 "살아 있는" 객체와 "죽은" 객체를 구분하며, 회수 단계에서는 "죽은" 객체의 메모리를 해제하여 이를 재사용 가능하게 만듭니다.

3. 세대별 메모리 관리 (Generational Memory Management)

.NET의 가비지 컬렉터는 세대별 메모리 관리 방식을 사용하여 메모리를 효과적으로 관리합니다. 이 방식은 대부분의 객체는 생성된 직후에 빠르게 소멸된다는 관찰에 기반하고 있습니다. 따라서 최근에 생성된 객체는 오래된 객체보다 더 자주 수집됩니다.

1. Generation 0:

  • 가장 최근에 할당된 객체들이 속하는 세대입니다.
  • 이 세대의 객체들은 다른 세대의 객체들보다 가장 빈번하게 GC에 의해 검사되고 수집됩니다.
  • 객체가 처음 생성되면 이 세대에 할당되며, 가비지 컬렉터가 실행되면서 살아남은 객체는 다음 세대인 Generation 1로 승격됩니다.

2. Generation 1:

  • Generation 0에서 살아남은 객체들이 승격되는 세대입니다.
  • 이 세대의 객체들은 Generation 0보다는 덜 빈번하지만, Generation 2보다는 더 자주 검사되고 수집됩니다.
  • 여기서도 GC가 실행되면서 살아남은 객체는 다음 세대인 Generation 2로 승격됩니다.

3. Generation 2:

  • Generation 1에서 살아남은 객체들이 승격되는 세대입니다.
  • 이 세대의 객체들은 가장 오래된 객체들로, 가장 덜 빈번하게 검사되고 수집됩니다.
  • 그렇기 때문에 Generation 2에서의 가비지 컬렉션은 자주 발생하지 않습니다. Generation 2에서의 GC가 실행될 때는 전체 힙에 대한 메모리 수집이 발생하므로, 가장 부담이 큰 작업으로 간주됩니다.

이 세대별 관리 방식의 장점은 메모리 수집 작업을 빈번하게 수행하는 Generation 0의 영역이 상대적으로 작기 때문에 수집 작업의 부담을 크게 줄일 수 있습니다. 또한, 오래된 객체들은 비교적 덜 빈번하게 수집되므로 전체 힙의 메모리 수집 부담도 줄어듭니다.

4. Compactation (압축)

메모리 내에서 발생하는 조각화는 시간이 흐르면서 가비지 컬렉션 과정에서 "살아 있는" 객체와 "죽은" 객체가 서로 뒤섞이게 됨으로써 발생합니다. 이렇게 조각화된 메모리는 새로운 큰 객체를 할당하기 어려운 상황을 초래할 수 있습니다. 이러한 메모리 조각화를 해결하기 위해 Compactation 또는 압축이라는 과정이 사용됩니다.

1. 객체 재배치

"살아 있는" 객체들을 메모리의 시작 부분으로 이동시켜, 이 객체들이 연속적인 메모리 공간에 위치하도록 합니다. 이로 인해 메모리의 나머지 부분에는 연속적인 빈 공간이 형성됩니다.

2. 참조 업데이트

객체의 위치가 바뀌게 되면, 해당 객체를 참조하고 있는 다른 객체나 변수들의 참조 값을 해당 객체의 새로운 주소로 업데이트해야 합니다.

3. 메모리 해제

이전에 "죽은" 객체들이 차지하고 있던 메모리 영역은 이제 빈 공간이 되므로, 이 영역은 해제되어 새로운 객체 할당을 위해 재사용될 수 있습니다.

장점

  1. 연속적인 메모리 할당이 가능해집니다.
  2. 객체들이 메모리 내에서 근접하게 위치하게 되면, CPU 캐시의 효율성이 향상될 수 있습니다.

단점

  1. 압축 과정은 추가적인 처리 오버헤드를 발생시킵니다.
  2. 큰 힙 메모리에서의 압축은 상당한 시간이 소요될 수 있으며, 이로 인해 응답 시간에 영향을 줄 수 있습니다.

요약: Compactation은 메모리 조각화 문제를 해결하기 위한 과정으로, "살아 있는" 객체들을 연속적인 메모리 공간에 재배치하는 것을 중점으로 합니다. 하지만, 이 과정은 추가적인 오버헤드와 시간 지연을 발생시킬 수 있습니다.

반응형

'Programming > Blah Blah' 카테고리의 다른 글

비주얼 스튜디오 확장(vsix)  (0) 2018.05.05
.tt 확장자 예제  (0) 2018.04.19
배치파일  (0) 2018.04.10
신경망 첫걸음  (0) 2018.03.25
CCU와 MCU란?  (0) 2018.01.25
[Visual Studio Code] 단축키 및 플러그인 추천  (0) 2018.01.13
Readme Driven Development(RDD)  (0) 2018.01.12
워크 맵핵 만들기  (26) 2017.12.25