가비지 컬렉터
가비지 컬렉터(Garbage Collector)는 .NET 애플리케이션의 자동 메모리 관리자이다. C++ 같은 프로그래밍 언어에서는 개발자가 new 키워드로 할당한 메모리를 반드시 delete 키워드로 직접 해제해야 했으나, C# 언어에서는 가비지 컬렉터가 더 이상 사용되지 않는 객체를 스스로 찾아내어 메모리를 회수한다.
-
철학: 개발자는 비즈니스 로직에만 집중하라. 메모리 관리는 엔진이 책임진다.
-
장점: 메모리 누수(Memory Leak) 방지, 해제된 메모리 재사용(Dangling Pointer), 객체 배치 최적화 등이 있다.
가비지 컬렉터와 가비지 컬렉션
두 용어는 자주 혼용되지만, 엄밀히 따지면 주체와 행위의 차이가 있다.
| 구분 | 가비지 컬렉터(Garbage Collector) | 가비지 컬렉션(Garbage Collection) |
|---|---|---|
| 정의 | 메모리를 관리하는 소프트웨어 엔진(엔티티) | 쓰레기 객체를 수집하여 메모리를 비우는 과정(이벤트) |
| 역할 | 힙 메모리 감시, 수집 시점 결정, 알고리즘 실행 | 실제 메모리 스캔, 마킹, 컴팩션 수행 |
관리되는 힙
C# 언어에서 모든 참조 타입(Class, String 등)의 객체는 관리되는 힙(Managed Heap)이라는 특별한 메모리 공간에 할당된다.
-
다음 객체 포인터(NextObjPtr): 관리되는 힙은 메모리를 할당할 때 단순히 포인터를 이동시키는 방식으로 동작한다. 이는 C++ 언어의 malloc 방식보다 훨씬 빠르며, 포인터 뒤에 충분한 공간이 있으면 즉시 할당하고 포인터를 이동시키는 원리이다.
-
할당 과정: new 키워드를 만나면 가비지 컬렉션은 관리되는 힙에 공간이 있는지 확인하고, 포인터 위치에 객체를 놓은 후 포인터를 새로운 위치로 갱신한다.
가비지 컬렉션 트리거
가비지 컬렉션은 아무 때나 돌아가는 것이 아니라 특정 조건이 충족될 때 중단과 수집을 시작한다.
-
메모리 부족: 시스템의 물리적 메모리가 부족하다는 운영체제 신호가 올 때 실행된다.
-
임계치 초과: 관리되는 힙에 할당된 객체들의 용량이 세대 별로 정해진 임계치(Threshold)를 넘었을 때 실행된다.
-
명시적 호출: 개발자가 코드에서 GC.Collect() 메소드를 직접 호출할 때 실행된다.
쓰레기를 판별하는 기준: 루트
가비지 컬렉션은 어떤 객체가 가비지(Garbage)인지 확인하는데 그 기준은 누군가 이 객체를 여전히 가리키고 있는가이다. 이를 판단하기 위해 루트에서 부터 조사를 시작한다.
-
루트의 종류: 스택 변수, 정적 변수, CPU 레지스터 등이 존재한다.
-
스택 변수(Stack Variables): 현재 실행 중인 메소드 내의 지역 변수나 매개 변수이다.
-
정적 변수(Static Variables): static 키워드로 선언되어 애플리케이션 수명 내내 존재하는 변수이다.
-
CPU 레지스터(CPU Registers): 현재 계산을 위해 CPU 내부에서 참조 중인 객체 주소이다.
-
최종 큐(Finalization Queue): 종료자가 실행되기를 기다리는 객체들이다.
-
-
작동 원리: 가비지 컬렉션은 이 루트들로부터 시작하여 연결된 모든 객체를 추적한다. 이 추적 망에 걸리는 객체는 생존 상태이며, 루트로부터 어떤 경로로도 도달할 수 없는 객체는 가비지로 간주되어 수집 대상이 된다.
가비지 컬렉션 3단계
가장 기초적인 가비지 컬렉션 알고리즘인 마크 앤 스윕(Mark-Sweep Compact)은 다음의 단계를 거친다.
-
마킹(Marking): 모든 루트를 순회하며, 살아있는 객체에 사용 중이라는 표시를 한다.
-
스윕(Sweep): 마킹되지 않은 객체들이 차지한 메모리 공간을 비운다.
-
압착(Compact): 살아남은 객체들은 메모리의 앞부분으로 밀어 넣는다. 이렇게 하면 메모리 사이의 파편화가 사라지고 뒤쪽에는 연속된 빈 공간이 생긴다.