# [[Node.js|Node.js]]

## 📌 Brief Summary
Node.js는 구글의 V8 자바스크립트 엔진을 기반으로 구축되어 서버 측에서 자바스크립트를 실행할 수 있게 해주는 런타임 환경입니다 [1]. 전통적인 다중 프로세스 서버와 달리 단일 프로세스로 장시간 실행되는 특징을 가지며, 이로 인해 누수된 참조가 프로세스 수명 동안 지속적으로 누적될 수 있습니다 [2, 3]. Node.js의 메모리 할당 및 가비지 컬렉션(GC)은 전적으로 내장된 V8 엔진의 자동 메모리 관리 메커니즘에 의존합니다 [1, 4].

## 📖 Core Content
* **메모리 구조 (Memory Structure):** Node.js 프로세스의 메모리는 크게 스택(Stack)과 힙(Heap)으로 나뉩니다 [1, 5]. 스택은 로컬 변수, 함수 호출 정보 등을 저장하며 후입선출(LIFO) 방식으로 빠르게 작동합니다 [5]. 반면 힙은 자바스크립트 객체, 배열, 함수 등 동적 데이터가 할당되는 곳으로, 가비지 컬렉터에 의해 관리되며 크기가 고정되어 있지 않습니다 [5, 6].
* **가비지 컬렉션 (Garbage Collection):** Node.js를 구동하는 V8 엔진은 "대부분의 객체는 일찍 죽는다"는 세대별 가설(Generational hypothesis)을 바탕으로 힙을 관리합니다 [7, 8]. 수명이 짧은 객체는 'New Space(젊은 세대)'에 할당되어 'Scavenger(Minor GC)'에 의해 자주 수집되며, 여러 번의 GC 주기를 살아남은 장기 체류 객체는 'Old Space(오래된 세대)'로 이동하여 'Mark-Sweep-Compact(Major GC)' 알고리즘을 통해 덜 빈번하게 수집됩니다 [7, 9, 10].
* **프로덕션 환경의 메모리 누수 패턴:** Node.js는 단일 프로세스로 오래 실행되기 때문에 도달 불가능해야 할 객체가 코드상의 참조로 인해 메모리에 계속 남아있는 경우 심각한 누수가 발생합니다 [2, 11]. 주요 누수 원인으로는 누적된 `EventEmitter` 리스너, 클로저 변수 유지, 무제한으로 커지는 인메모리 캐시, 해제되지 않은 타이머(`setInterval`), 닫히지 않은 스트림이나 소켓, 그리고 `AsyncLocalStorage` 컨텍스트 누수 등이 있습니다 [12-14].
* **메모리 튜닝 및 모니터링:** 개발자는 `process.memoryUsage()` 메서드를 통해 RSS(상주 집합 크기), heapTotal, heapUsed, external 등의 메모리 사용량을 모니터링할 수 있습니다 [15]. 또한 `--max-old-space-size`(Old Space 제한), `--max-semi-space-size`(New Space 제한), `--gc-interval`, `--expose-gc`와 같은 명령줄 플래그를 사용하여 힙 크기를 튜닝하거나 수동으로 GC를 제어할 수 있습니다 [16-19].
* **진단 및 추적:** GC 활동은 `--trace-gc` 플래그나 `v8` 모듈, 성능 훅(performance hooks)을 통해 프로그램적으로 추적할 수 있습니다 [20-22]. 메모리 누수가 의심될 때는 Chrome DevTools의 Memory 탭과 `--inspect` 플래그, 또는 `heapdump` 패키지를 이용해 힙 스냅샷을 캡처하고 객체의 유지 경로(Retaining path)를 분석하여 원인을 식별합니다 [23-25].

## 🔗 Knowledge Connections
- **Related Topics:** [[V8 JavaScript Engine|V8 JavaScript Engine]], [[Garbage Collection|Garbage Collection]], [[Memory Management|Memory Management]]
- **Projects/Contexts:** Node.js Memory Tuning and Diagnostics, Electron and the V8 Memory Cage
- **Contradictions/Notes:** 수동으로 가비지 컬렉션을 트리거하기 위해 `--expose-gc` 플래그를 사용하여 `global.gc()`를 호출할 수 있지만, 이는 V8의 자동 GC 알고리즘을 비활성화하는 것이 아니며 남용할 경우 성능 저하를 일으킬 수 있으므로 주의해서 사용해야 합니다 [19, 26]. 또한, 전통적인 가비지 컬렉터는 애플리케이션을 완전히 멈추는(stop-the-world) 문제를 유발했으나, V8의 최신 Orinoco GC는 메인 스레드의 멈춤을 최소화하기 위해 병렬(Parallel), 증분(Incremental), 동시(Concurrent) 기법을 도입하여 백그라운드에서 메모리를 회수합니다 [27-30].

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