Waylog Blog

가비지 컬렉션(Garbage Collection) 동작 원리 비교

CS

C언어를 할 때는 malloc 하고 free를 안 해서 메모리 누수(Memory Leak)가 나는 게 일상이었습니다. 하지만 Java, JavaScript 같은 현대 언어들은 **Garbage Collector (GC)**라는 청소부를 고용했습니다. 개발자가 메모리 해제에 신경 쓰지 않아도 되게 말이죠.

1. 기본 원리: 도달 가능성 (Reachability)

GC는 "이 객체가 아직 필요한가?"를 판단하기 위해 Root Set(전역 변수, 스택의 변수 등)에서부터 참조를 따라가 봅니다. 연결되어 있다면 Reachable, 끊겨 있다면 Unreachable로 간주하고 수거합니다.

2. Mark-and-Sweep 알고리즘

가장 고전적인 방식입니다.

  1. Mark: 사용 중인 객체에 깃발을 꽂습니다.
  2. Sweep: 깃발 없는 놈들을 메모리에서 쓸어버립니다. 단점은 청소하는 동안 프로그램이 멈춘다는 것(Stop-the-world)입니다.

3. Generational GC (세대별 GC)

"대부분의 객체는 금방 죽는다"는 가설(Weak Generational Hypothesis)에 기반합니다.

  • Young Generation: 갓 태어난 객체들이 있습니다. 여기서 자주 청소(Minor GC)가 일어납니다. 살아남은 놈들은 Old 영역으로 승진(Promotion)합니다.
  • Old Generation: 오래 살아남은 객체들이 모입니다. 꽉 차면 대청소(Major GC)를 합니다. 이때 렉이 걸립니다.

4. V8 엔진(Chrome, Node.js)의 최적화

V8은 Orinoco라는 프로젝트를 통해 GC를 병렬화하고, 메인 스레드를 멈추지 않으면서 조금씩 청소하는 Incremental Marking을 도입하여 뚝뚝 끊기는 현상을 최소화했습니다.

5. 메모리 누수 주의

GC가 있어도 누수는 발생합니다.

  • console.log에 찍힌 객체는 GC 되지 않습니다.
  • 전역 변수에 담긴 데이터, 해제되지 않은 타이머나 이벤트 리스너가 주범입니다.

"자동"이라고 해서 "방치"해도 된다는 뜻은 아닙니다.