Node.js 메모리 옵션
테스트 케이스를 로컬에서 돌릴때는 문제 없었는데, 운영에서 실행하니 메모리 부족 오류가 발생했습니다.
오류 로그
<--- Last few GCs --->
[1516:0x56a6a70] 93701 ms: Scavenge 1952.6 (2079.2) -> 1939.9 (2079.2) MB, 5.0 / 0.3 ms (average mu = 0.309, current mu = 0.242) task
[1516:0x56a6a70] 93740 ms: Scavenge 1953.1 (2079.2) -> 1940.6 (2079.2) MB, 5.0 / 0.2 ms (average mu = 0.309, current mu = 0.242) task
[1516:0x56a6a70] 93799 ms: Scavenge 1956.1 (2079.2) -> 1941.9 (2079.2) MB, 5.5 / 0.7 ms (average mu = 0.309, current mu = 0.242) allocation failure
<--- JS stacktrace --->
FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory
1: 0xb09c10 node::Abort() [node]
2: 0xa1c193 node::FatalError(char const*, char const*) [node]
3: 0xcf8dde v8::Utils::ReportOOMFailure(v8::internal::Isolate*, char const*, bool) [node]
4: 0xcf9157 v8::internal::V8::FatalProcessOutOfMemory(v8::internal::Isolate*, char const*, bool) [node]
5: 0xeb09f5 [node]
6: 0xeb14d6 [node]
7: 0xebf9fe [node]
8: 0xec0440 v8::internal::Heap::CollectGarbage(v8::internal::AllocationSpace, v8::internal::GarbageCollectionReason, v8::GCCallbackFlags) [node]
9: 0xec33be v8::internal::Heap::AllocateRawWithRetryOrFailSlowPath(int, v8::internal::AllocationType, v8::internal::AllocationOrigin, v8::internal::AllocationAlignment) [node]
10: 0xe84632 v8::internal::Factory::AllocateRaw(int, v8::internal::AllocationType, v8::internal::AllocationAlignment) [node]
11: 0xe7ef4c v8::internal::FactoryBase<v8::internal::Factory>::AllocateRawArray(int, v8::internal::AllocationType) [node]
12: 0xe7f025 v8::internal::FactoryBase<v8::internal::Factory>::NewFixedArrayWithFiller(v8::internal::Handle<v8::internal::Map>, int, v8::internal::Handle<v8::internal::Oddball>, v8::internal::AllocationType) [node]
13: 0x10e6c5b v8::internal::MaybeHandle<v8::internal::OrderedHashSet> v8::internal::OrderedHashTable<v8::internal::OrderedHashSet, 1>::Allocate<v8::internal::Isolate>(v8::internal::Isolate*, int, v8::internal::AllocationType) [node]
14: 0x109c878 v8::internal::KeyAccumulator::AddKey(v8::internal::Handle<v8::internal::Object>, v8::internal::AddKeyConversion) [node]
15: 0x10a14e1 v8::internal::KeyAccumulator::CollectOwnPropertyNames(v8::internal::Handle<v8::internal::JSReceiver>, v8::internal::Handle<v8::internal::JSObject>) [node]
16: 0x10a1a8f v8::internal::KeyAccumulator::CollectOwnKeys(v8::internal::Handle<v8::internal::JSReceiver>, v8::internal::Handle<v8::internal::JSObject>) [node]
17: 0x10a1d23 v8::internal::KeyAccumulator::CollectKeys(v8::internal::Handle<v8::internal::JSReceiver>, v8::internal::Handle<v8::internal::JSReceiver>) [node]
18: 0x10a2733 v8::internal::KeyAccumulator::GetKeys(v8::internal::Handle<v8::internal::JSReceiver>, v8::internal::KeyCollectionMode, v8::internal::PropertyFilter, v8::internal::GetKeysConversion, bool, bool) [node]
19: 0x10896bc v8::internal::JSReceiver::DefineProperties(v8::internal::Isolate*, v8::internal::Handle<v8::internal::Object>, v8::internal::Handle<v8::internal::Object>) [node]
20: 0xd7c726 v8::internal::Builtin_ObjectDefineProperties(int, unsigned long*, v8::internal::Isolate*) [node]
21: 0x15f2199 [node]
Aborted (core dumped)
OOM 오류 수정 방법
OOM 오류를 해결하려면 Node.js 명령줄 옵션을 사용하여 메모리 제한을 명시적으로 구성하기만 하면 됩니다.
# -max-old-space-size=<size_megabytes>
# 사용 예제
$ node --max-old-space-size=2048 index.js #increase to 2GB
$ node --max-old-space-size=3072 index.js #increase to 3GB
$ node --max-old-space-size=4096 index.js #increase to 4GB
$ node --max-old-space-size=5120 index.js #increase to 5GB
$ node --max-old-space-size=6144 index.js #increase to 6GB
$ node --max-old-space-size=7168 index.js #increase to 7GB
$ node --max-old-space-size=8192 index.js #increase to 8GB
사용 가이드
메모리 값의 경우 환경, Node.js버젼의 기본값에 의존하는 대신 --max-old-space-size
를 항상 명시적으로 설정하는 것이 좋습니다.
예를 들어 메모리가 2GB
인 시스템에서는 이 값을 1536(1.5GB)
으로 설정하여 일부 메모리를 다른 용도로 남겨두어 메모리 스와핑을 방지
하는 것이 좋습니다.
또한 작은 컴퓨터(Raspberry Pi 보드 등)에서 Node.js를 사용하여 간단한 웹 서버를 실행하는 경우 Node.js가 소중한 메모리를 너무 많이 사용하지 않도록 128MB 정도의 작은 값으로 --max-old-space-size
를 이용해 설정할 수 있습니다.
gc 등 추가 옵션
대용량 접속(600k concurrent websocket connections on AWS using Node.js)의 처리를 진행할 경우 gc(garbage collector)로 인해 몇초간 서버가 멈추는 현상이 발생할 수 있습니다.
gc를 수동으로 전환하기 위해서는 다음 옵션을 사용합니다.
$ node --trace-gc --expose-gc --nouse-idle-notification --max-new-space-size=2048 --max-old-space-size=8192 index.js
--nouse-idle-notification
: gc 자동 실행 방지--expose-gc
: gc() 함수를 자바스크립트에서 직접 호출--trace-gc
: gc 표출 Tracing garbage collection--max-new-space-size
: 피크 성능을 줄여버리지만, 멈춤 시간을 짧게 가져가야 한다면 해당 값을 2048(2G) 정도로 작게 가져가면, 100ms 정도로 짧아짐--noincremental-marking
: 피크 성능이 중요하고 서버 멈춤을 늘려도 된다면 사용, 1GB 당 1초의 서버 멈춤 가능--max-semi-space-size
: 단기 객체용으로 예약된 최대 힙 크기--max-executable-size
: 실행 코드용으로 예약된 힙의 최대 크기(적시 컴파일된 JavaScript의 기본 코드 결과)