# [[Node.js Memory Management|Node.js Memory Management]]

## 📌 Brief 정Summary
Node.js 메모리 관리는 구글의 V8 자바스크립트 엔진에 의해 수행되며, 스택(Stack)과 힙(Heap) 메모리 구조를 기반으로 데이터를 관리합니다 [1-3]. V8은 '대부분의 객체는 일찍 죽는다(Generational Hypothesis)'는 가설에 기반한 세대별 가비지 컬렉션(Generational Garbage Collection)을 사용하여 자동으로 사용되지 않는 메모리를 회수합니다 [4-6]. 단일 프로세스로 장시간 실행되는 Node.js의 특성상 참조가 유지된 채 힙에 쌓이는 객체들은 메모리 누수와 OOM(Out of Memory) 충돌의 주원인이 되므로, 메모리 할당 패턴을 이해하고 적절한 튜닝과 디버깅을 수행하는 것이 필수적입니다 [7-9].

## 📖 Core Content
**V8 메모리 아키텍처 (Stack & Heap)**
* Node.js를 실행하는 V8 엔진은 메모리를 스택(Stack)과 힙(Heap)으로 나누어 관리합니다 [1, 2]. 
* 스택은 정적 데이터, 메서드/함수 프레임, 원시 값, 힙에 있는 객체를 가리키는 포인터를 LIFO(Last In, First Out) 방식으로 저장하며, 운영체제에 의해 매우 빠르게 자동 관리됩니다 [1, 10-12].
* 힙은 실행 시간에 크기가 결정되는 동적 객체가 저장되는 곳으로 가비지 컬렉터(GC)의 주요 관리 대상이 됩니다 [10, 13, 14]. 힙 내부는 객체의 수명과 목적에 따라 New-space(Young generation), Old-space(Old generation), Large-object-space, Code-space, Map-space 등으로 세분화됩니다 [14-16].

**세대별 가비지 컬렉션 (Generational Garbage Collection)**
* **Minor GC (Scavenger):** New-space를 관리하는 빠르고 빈번한 컬렉터입니다 [17, 18]. New-space는 절반씩 To-space와 From-space로 나뉘며(Cheney's algorithm), 할당 포인터가 공간 끝에 도달하면 살아있는 객체만 To-space로 복사하고 죽은 객체를 버립니다 [18-20]. 이 과정을 두 번 생존한 객체는 Old-space로 승격(Promotion)됩니다 [17, 19, 20].
* **Major GC (Mark-Sweep-Compact):** Old-space가 일정 한도에 도달하면 실행되며, Mark-Sweep-Compact 알고리즘을 사용합니다 [17, 21-23]. 루트(스택, 전역 객체 등)에서 시작해 도달 가능한 객체를 탐색하여 마킹(Marking)하고, 도달할 수 없는 영역을 회수(Sweeping)하며, 필요 시 살아남은 객체를 모아 단편화를 줄이는 압축(Compacting)을 수행합니다 [21, 24-27].
* **Orinoco 프로젝트:** 전통적인 GC의 단점인 긴 일시 정지(Stop-the-world) 문제를 해결하기 위해 도입된 V8의 GC 아키텍처입니다 [28-30]. 작업 스레드를 활용하여 GC 작업을 병렬(Parallel), 점진적(Incremental), 동시적(Concurrent)으로 수행하여 메인 스레드의 부하와 지연을 최소화합니다 [31-37].

**메모리 누수 (Memory Leaks) 발생 패턴 및 분석**
* Node.js에서 메모리 누수는 객체가 유실된 것이 아니라 개발자가 의도치 않게 참조(Reference)를 유지하여 가비지 컬렉터가 이를 살아있는 것으로 간주할 때 발생합니다 [8, 38, 39].
* 정상적인 GC 사이클을 거치는 애플리케이션은 톱니바퀴(Sawtooth) 형태의 메모리 사용 패턴을 보이지만, 누수가 있는 경우 해제되지 않고 계속 증가만 하는 라쳇(Ratchet) 패턴을 보입니다 [40-42].
* 주요 누수 원인으로는 이벤트 리스너 누적(예: `EventEmitter` 경고), 해제되지 않은 타이머/인터벌(Timer Drift), 클로저 변수 보존(Closure Retention), 한도 없는 인메모리 캐시, 종료되지 않은 스트림(Streams) 등이 있습니다 [39, 43-46].

**모니터링 및 메모리 튜닝 (Monitoring and Tuning)**
* 코드 상에서 `process.memoryUsage()`를 통해 rss, heapTotal, heapUsed 등의 메모리 지표를 추적할 수 있으며 [47, 48], `--trace-gc` 플래그나 V8 모듈, 퍼포먼스 훅(Performance Hooks)을 통해 GC 활동 로그를 분석할 수 있습니다 [49-51].
* 발견하기 힘든 누수 분석 시에는 Chrome DevTools의 Memory 패널을 이용하여 힙 스냅샷(Heap Snapshots)을 비교하거나 Allocation Timeline을 기록하여 누수 대상을 추적합니다 [40, 52-57].
* Node.js 실행 시 플래그를 통해 메모리를 제어할 수 있습니다. `--max-old-space-size`로 Old-space 한도를 늘리거나, `--max-semi-space-size`로 New-space 크기를 키울 수 있으며, `--expose-gc`를 설정하면 애플리케이션에서 `global.gc()`를 통해 수동으로 GC를 유발할 수 있습니다 [58-62].
* 포인터 압축(Pointer Compression) 기술로 인해 64비트 시스템에서도 V8 힙은 최대 4GB로 제한될 수 있으며, 이를 초과할 경우 빈번한 GC 발생 및 OOM이 일어날 수 있습니다 [63-66].

## 🔗 Knowledge Connections
- **Related Topics:** [[V8 JavaScript Engine|V8 JavaScript Engine]], [[Garbage Collection|Garbage Collection]], [[Orinoco GC|Orinoco GC]], [[Memory Leaks|Memory Leaks]], [[Pointer Compression|Pointer Compression]]
- **Projects/Contexts:** [[Node.js Production Monitoring|Node.js Production Monitoring]], [[Chrome DevTools Memory Profiling|Chrome DevTools Memory Profiling]]
- **Contradictions/Notes:** `--expose-gc` 옵션을 사용해 코드 내에서 수동으로 GC(`global.gc()`)를 호출하여 메모리를 회수할 수는 있으나, 과도하게 사용하면 프로그램 성능 저하(Performance degradation)를 초래할 수 있으므로 주의해서 사용해야 한다고 경고합니다 [62, 67].

---
*Last updated: 2026-04-19*