Files
2nd/10_Wiki/Topics/Architecture/Garbage_Collection.md
T

14 KiB

category, tags, title, last_updated
category tags title last_updated
Unified
auto-consolidated
technical-documentation
Garbage Collection(가비지 컬렉션)|Garbage Collection(가비지 컬렉션
2026-05-02

Garbage Collection(가비지 컬렉션)

📌 Brief Summary

가비지 컬렉션(Garbage Collection)은 애플리케이션의 메모리 고갈을 방지하기 위해 더 이상 필요하지 않거나 도달할 수 없는 객체가 차지한 메모리를 자동으로 식별하여 회수하는 프로세스입니다 [1-3]. 이 기술은 프로그래머가 직접 메모리를 관리하는 부담을 덜어주고 메모리 누수와 같은 오류를 줄여주지만, 메모리 정리 작업 중 애플리케이션 실행이 멈추는 'Stop-the-world' 지연을 발생시킬 수 있습니다 [2, 4, 5]. 현대의 가비지 컬렉터들은 이러한 지연 시간을 최소화하기 위해 세대별 힙 구조(Generational Layout)와 병렬 및 동시 처리 알고리즘을 도입하여 성능을 최적화하고 있습니다 [6-8].


가비지 컬렉션(Garbage Collection, GC)은 사용되지 않는 구형 데이터를 메모리에서 해제하는 자바스크립트 엔진의 메모리 관리 프로세스입니다 [1]. 하지만 Three.js와 같은 실시간 3D 렌더링 환경에서는 빈번한 객체 생성이나 메모리 한계 초과로 인해 가비지 컬렉터가 작동할 경우, 프레임이 일시적으로 멈추는(Stuttering) 심각한 성능 저하가 발생할 수 있습니다 [1-3]. 또한, Three.js는 GPU 자원에 대해 자동으로 가비지 컬렉션을 수행하지 않기 때문에 개발자의 명시적인 메모리 관리가 필수적입니다 [4].


가비지 컬렉션(GC)은 애플리케이션에서 더 이상 필요하지 않거나 도달할 수 없는 객체가 차지한 메모리 영역을 식별하고 자동으로 회수하여 재사용할 수 있도록 하는 메모리 관리 프로세스입니다 [1, 2]. 프로그래머가 직접 메모리를 할당하고 해제해야 하는 복잡성을 줄여주고 메모리 누수와 같은 오류를 방지하는 데 도움을 줍니다 [3]. 하지만 메모리 관리에 대한 통제권을 잃게 되며, 시스템 실행을 멈추게 하는 예측 불가능한 일시 정지(Stop-the-world)를 유발할 수 있는 양날의 검과 같은 특성을 가집니다 [2-4].

📖 Core Content

  • 생존 객체 식별 원리 (Discovering Reachability) 가비지 컬렉터가 해결해야 할 핵심 문제는 죽은 메모리 영역을 식별하는 것입니다 [1]. 객체의 미래 접근 여부를 완벽히 예측하는 것은 정지 문제(Halting Problem)와 같아 불가능하므로, GC는 스택의 로컬 변수나 글로벌 객체와 같은 '루트(Root) 객체'부터 시작하여 포인터 체인을 통해 도달할 수 있는 객체만을 '생존(Live)' 상태로 근사하여 판단하고, 도달 불가능한 모든 객체를 '가비지(Garbage)'로 간주합니다 [1, 9, 10].

  • 세대별 가설과 힙 메모리 구조 (Generational Collection & Heap Organization) 대부분의 객체는 생성 후 얼마 지나지 않아 죽는다는 '세대별 가설(Generational Hypothesis)'을 기반으로, V8과 같은 엔진은 힙 메모리를 세대별로 나누어 관리합니다 [6, 11, 12].

    • New Space (Young Generation): 대부분의 새 객체가 할당되는 작고 빠른 공간입니다 [6, 13]. 주기적으로 마이너 가비지 컬렉션이 빠르게 수행됩니다 [6].
    • Old Space (Old Generation): New Space에서 수행된 가비지 컬렉션을 두 번 살아남은 객체들이 승격(Promotion)되어 이동하는 공간입니다 [6, 14, 15].
  • 마이너 가비지 컬렉션 (Scavenger) Young Generation을 관리하는 고속 알고리즘입니다 [16, 17]. New Space를 'To-Space'와 'From-Space'라는 동일한 크기의 두 반공간(Semi-space)으로 분할하는 Cheney의 알고리즘(Scavenge)을 사용합니다 [16, 18]. 할당 공간이 가득 차면, 활성 객체를 추적하여 빈 공간(To-Space)이나 Old Space로 대피(Evacuate)시키면서 메모리를 압축합니다 [18, 19]. 이후 남은 From-Space의 쓰레기 데이터를 한 번에 비우고 두 공간의 역할을 바꿉니다 [18, 19].

  • 메이저 가비지 컬렉션 (Mark-Sweep-Compact) 수백 메가바이트의 데이터를 포함할 수 있는 Old Space를 관리하기 위해 메이저 GC는 주로 세 가지 단계를 거칩니다 [20, 21].

    • Marking (표시): 깊이 우선 탐색(DFS)을 통해 힙 내의 활성 객체를 발견합니다 [22]. 객체를 흰색(미발견), 회색(발견되었으나 이웃 객체 미처리), 검은색(발견 및 이웃 처리 완료)의 3색 마킹 상태로 분류하여 추적합니다 [23, 24].
    • Sweeping (쓸기): 마킹되지 않은 죽은 객체의 연속된 메모리 범위를 스캔하여 Free list(여유 공간)로 변환하여 후속 할당에 대비합니다 [24-26].
    • Compacting (압축): 메모리 단편화(Fragmentation)를 줄이기 위해 생존 객체들을 빈 공간으로 마이그레이션합니다 [24, 27, 28]. 이는 비용이 많이 드는 작업이지만 실제 메모리 사용량을 대폭 줄여줍니다 [27, 29].
  • 현대 GC의 성능 최적화 (Orinoco 등) 수백 밀리초에 달하던 과거의 'Stop-the-world' 일시 정지 현상을 줄이기 위해 현대적인 GC는 다양한 기법을 동원합니다 [30, 31].

    • Parallel (병렬 처리): 메인 스레드와 여러 보조 스레드가 GC 작업을 동시에 분담하여 총 정지 시간을 줄입니다 [32, 33].
    • Incremental (점진적 처리): 자바스크립트 실행 중간중간에 작은 단위로 GC 작업을 번갈아 수행(Interleaving)하여 응답성을 유지합니다 [30, 32, 34].
    • Concurrent (동시 처리): 메인 스레드가 자바스크립트를 계속 실행하는 동안 보조 스레드들이 백그라운드에서 전적으로 Marking 및 Sweeping 작업을 수행합니다 [7, 32, 35].

  • GPU 자원의 명시적 해제 필요성: Three.js는 지오메트리, 머티리얼, 텍스처 등의 GPU 자원을 자동으로 가비지 컬렉트하지 않습니다 [4]. 따라서 단일 4K 텍스처가 64MB 이상의 VRAM을 차지하는 등 메모리 누수를 방지하려면, 사용이 끝난 자원은 반드시 .dispose() 메서드를 호출하여 명시적으로 GPU 메모리에서 해제해야 합니다 [4].
  • 프레임 멈춤(GC Pauses) 현상: useFrame과 같은 렌더링 루프 내부에서 객체를 계속 생성하거나, 모바일 기기의 시스템 메모리 한계를 초과하는 무거운 텍스처를 사용하면 가비지 컬렉션이 강제로 트리거되어 프레임 멈춤 현상과 스터터링이 발생합니다 [2, 3].
  • 동적 버퍼 할당과 TypedArray 부하: 대규모 인스턴스 렌더링 환경에서 버퍼 용량을 초과해 동적으로 버퍼를 계속 확장할 경우, 수십 메가바이트 단위의 TypedArray가 빈번하게 생성되고 파괴됩니다 [1]. 자바스크립트 엔진이 이 구형 배열 데이터를 해제하는 과정에서 가비지 컬렉터가 작동하여 메인 스레드의 점유 시간을 늘리고 프레임 드랍을 유발합니다 [1, 5].
  • 객체 풀링(Object Pooling)을 통한 GC 부하 완화: 가비지 컬렉션으로 인한 할당 오버헤드와 멈춤 현상을 방지하기 위해서는, 총알이나 파티클처럼 자주 생성되고 파괴되는 엔티티에 대해 새로운 객체를 생성하는 대신 '객체 풀(Pool)'을 미리 구성하여 재사용하는 방식이 강력히 권장됩니다 [6].

  • 도달 가능성(Reachability)과 생존 객체 식별 가비지 컬렉터는 '도달 가능성'을 객체 생존의 지표로 사용합니다 [5]. 전역 객체, 로컬 변수(스택), 웹 브라우저의 DOM 요소 등과 같은 루트(Root) 객체이거나, 다른 생존 객체로부터 참조(Pointer) 체인을 통해 도달할 수 있는 객체는 '생존(live)' 상태로 간주됩니다 [1, 6, 7]. 반면 루트에서 도달할 수 없는 모든 객체는 '죽은(dead)' 객체, 즉 가비지로 분류되어 메모리가 회수됩니다 [1, 6, 8, 9].

  • 세대별 가설과 힙(Heap) 메모리 구조 대부분의 객체는 생성된 직후 금방 죽는다는 '세대별 가설(Generational Hypothesis)'에 기반하여, V8과 같은 최신 엔진은 힙 메모리를 여러 세대로 분할합니다 [10-12]. 객체는 처음 매우 작고 할당이 빠른 '젊은 세대(Young Generation/New-space)'에 할당되며, 이곳에서 여러 번의 가비지 컬렉션 주기를 살아남은 객체만이 크기가 큰 '오래된 세대(Old Generation/Old-space)'로 승격(Promotion)됩니다 [10, 11, 13, 14].

  • 마이너 GC (Minor GC / Scavenge 연산) 젊은 세대의 메모리를 관리하기 위해 Scavenge(또는 Copy forward) 알고리즘이 자주 그리고 빠르게 실행됩니다 [15-18]. 이 알고리즘은 공간을 두 개의 동일한 반공간(From-space와 To-space)으로 나누어, 생존한 객체를 To-space로 복사하거나 오래된 세대로 승격시킨 후, From-space 전체를 비워버립니다 [15, 19-22]. 이 과정에서 객체들이 연속된 공간에 압축되므로 메모리 파편화가 방지됩니다 [15, 20, 21].

  • 메이저 GC (Major GC / Mark-Sweep-Compact) 오래된 세대를 수집할 때는 Mark-Sweep 및 Mark-Compact 알고리즘이 사용됩니다 [5, 23, 24].

    • 마킹(Marking): GC가 루트부터 객체 그래프를 깊이 우선 탐색(DFS)으로 추적하여 생존 객체를 식별합니다. 이때 객체는 발견 상태에 따라 흰색(미발견), 회색(발견되었으나 이웃 미처리), 검은색(완전 처리됨)으로 표시됩니다 [7, 25-27].
    • 스윕(Sweeping): 마킹되지 않은 죽은 객체의 범위를 스캔하여 빈 공간(Free list)으로 변환하고 회수합니다 [27-29].
    • 압축(Compacting): 메모리 파편화를 줄이기 위해 살아남은 객체들을 조각난 페이지에서 다른 페이지의 빈 공간으로 이주(Migration)시켜 모아줍니다 [2, 27, 30, 31].
  • 성능 최적화 기법 (지연 최소화) 과거 전통적인 GC 방식은 전체 애플리케이션 실행을 멈추는 'Stop-The-World' 일시 정지가 길어지는 문제가 있었습니다 [2, 32]. 이를 개선하기 위해 V8의 Orinoco 가비지 컬렉터 등은 메인 스레드와 헬퍼 스레드가 동시에 작업을 나누어 수행하는 병렬(Parallel) 처리, 메인 스레드의 JavaScript 실행 사이사이에 GC 작업을 작은 단위로 쪼개서 수행하는 점진적(Incremental) 처리, 그리고 메인 스레드 실행을 방해하지 않고 백그라운드에서 GC를 동시에 수행하는 동시(Concurrent) 기법을 도입하여 지연(Latency)과 버벅거림을 획기적으로 줄였습니다 [33-38].

⚖️ Trade-offs & Caveats

  • 과거 데이터와의 충돌: 자동화 엔진에 의해 매핑된 지식으로, 추후 정밀 검증 필요.
  • 정책 변화: Programming & Language 분야의 자동 자산화 수행.

  • 과거 데이터와의 충돌: 자동화 엔진에 의해 매핑된 지식으로, 추후 정밀 검증 필요.
  • 정책 변화: Graphics & Performance 분야의 자동 자산화 수행.

  • 과거 데이터와의 충돌: 자동화 엔진에 의해 매핑된 지식으로, 추후 정밀 검증 필요.
  • 정책 변화: Programming & Language 분야의 자동 자산화 수행.

🔗 Knowledge Connections

  • Related Topics: Mark-Sweep, Scavenger, Stop-the-world, Generational Hypothesis, memory Leak
  • Projects/Contexts: V8 JavaScript Engine, IBM ECLIPse OpenJ9, Orinoco Garbage Collector
  • Contradictions/Notes: 가비지 컬렉션은 메모리 누수를 대폭 줄여주지만, 프로그래머가 메모리 관리에 대한 전적인 통제권을 잃는다는 단점이 존재합니다(모바일 앱 등에서는 큰 문제점) [4, 5]. 또한 대안으로 거론되는 레퍼런스 카운팅(Reference Counting) 방식 역시 대규모 객체 그래프의 마지막 참조가 제거될 때 가비지 컬렉션과 유사한 예측 불가능한 정지 현상을 유발할 수 있어 완벽한 대체재는 아닙니다 [5].

Last updated: 2026-04-19




Last updated: 2026-04-19



  • Related Topics: 메모리 누수 (memory Leak), 힙 메모리 (Heap Memory), V8 엔진 (V8 Engine), Stop-the-world
  • Projects/Contexts: V8 Orinoco 프로젝트, Node.js, IBM ECLIPse OpenJ9
  • Contradictions/Notes: 가비지 컬렉션은 프로그래머에게서 수동 메모리 관리에 대한 부담을 덜어주어 대규모 애플리케이션의 메모리 누수나 오류를 획기적으로 줄여주는 장점이 있지만, 메모리 관리 시점에 대한 제어권을 잃게 되며 C/C++ 같은 언어와 비교할 때 포인터를 식별하고 마킹하는 등 런타임 오버헤드를 발생시킨다는 단점도 공존합니다 [3, 4].

Last updated: 2026-04-19