Computer >> 컴퓨터 >  >> 프로그래밍 >> JavaScript

JavaScript 가비지 컬렉션(GC)의 동작 원리 완벽 이해하기

가비지 컬렉션(Garbage Collection, GC)은 자동 메모리 관리 방식의 하나입니다. 가비지 컬렉터(이하 '컬렉터')는 프로그램에서 더 이상 사용되지 않는 객체가 점유하고 있는 메모리, 즉 '가비지(garbage)'를 회수하는 역할을 담당합니다.

다만, 어떤 메모리가 "더 이상 필요하지 않은가"를 자동으로 판별하는 일반적인 문제는 이론적으로 결정 불가능(undecidable)한 문제로 알려져 있습니다. 그렇기 때문에 실제 가비지 컬렉터들은 이 일반 문제에 대한 제한된 해법을 구현하여 동작합니다.

참조(Reference)라는 핵심 개념

가비지 컬렉션 알고리즘이 기반으로 하는 핵심 개념은 바로 참조(reference)입니다. 메모리 관리의 맥락에서 어떤 객체가 다른 객체에 명시적으로든 암묵적으로든 접근할 수 있다면, 전자가 후자를 '참조한다'고 표현합니다.

예제 코드

let a = []
function addToA() {
    let x = {name: "John"}
    a.push(x)
}
console.log(a[0])

실행 결과

{name: "John"}

위 예제에서 변수 x는 함수 실행이 끝난 후 스코프(scope)를 벗어나 더 이상 직접 접근할 수 없습니다. 하지만 배열 a를 통해서는 여전히 접근 가능합니다. 즉, 이 객체는 참조가 존재하는 한 메모리에 유지되어야 합니다. 만약 배열에서 해당 요소를 제거(pop)하면 참조가 사라지므로, 이 객체는 더 이상 필요 없어지며 가비지 컬렉션의 대상이 됩니다.

가비지 컬렉션의 주요 알고리즘

가비지 컬렉터는 크게 다음 두 가지 알고리즘을 사용해 동작합니다.

1. 참조 카운팅(Reference-counting) 방식

객체를 가리키는 참조가 0개라면, 그 객체는 '가비지', 즉 수집(collectible) 대상으로 판단됩니다. 이 방식은 과거 구형 브라우저에서 사용되었습니다.

그러나 이 방식에는 치명적인 단점이 있습니다. 순환 참조(circular reference)가 발생하면 두 객체가 서로를 계속 참조하기 때문에 참조 카운트가 0이 되지 못하고, 결과적으로 메모리에서 회수되지 않는 메모리 누수(memory leak)가 발생할 수 있습니다.

2. 마크 앤 스윕(Mark-and-sweep) 알고리즘

이 알고리즘은 "더 이상 필요 없는 객체"라는 정의를 "도달할 수 없는(unreachable) 객체"로 단순화합니다. 동작 방식은 다음과 같습니다.

  • GC는 루트(roots)라고 불리는 객체 집합을 기준점으로 삼습니다. JavaScript에서 루트는 전역 객체(global object)입니다.
  • 주기적으로 GC가 루트에서 출발하여, 루트가 참조하는 모든 객체를 재귀적으로 탐색합니다.
  • 이 과정에서 도달 가능한(reachable) 객체는 모두 살아있는 것으로 표시(mark)되고, 도달할 수 없는 객체는 수집(sweep)되어 메모리에서 해제됩니다.

마크 앤 스윕 방식은 순환 참조 문제를 해결할 수 있기 때문에, 현대 JavaScript 엔진(V8 등)에서 표준적으로 채택하고 있는 방식입니다.

마무리

JavaScript의 메모리 관리와 가비지 컬렉션에 대해 더 자세히 알아보려면 MDN 공식 문서(Memory Management - MDN Web Docs)를 참고하시기 바랍니다.