[node.js] FATAL ERROR: CALL_AND_RETRY_LAST Allocation failed - process out of memory 에러 원인, 해결방법
node.js 2017. 5. 19. 16:53node.js로 만든 서버 프로세스가 간헐적으로 아래와 같은 에러메시지와 함께 죽는 경우가 발생
FATAL ERROR: CALL_AND_RETRY_LAST Allocation failed - process out of memory
원인은 V8의 heap memory 할당 한계 사이즈를 넘어서는 메모리 할당이 일어났기 때문이었다.
해결방법은 간단하다.
--max_old_space_size 옵션값을 크게 설정해 heap memory 할당 한계 사이즈를 늘여주면 된다.
// 8G로 heap memory 할당 한계 사이즈 늘여서 실행. 옵션값 단위는 MB
node --max_old_space_size=8192 index.js
--max_old_space_size 값을 지정하지 않고 그냥 실행했을 경우 기본값은 64bit 기준으로 1.4G 정도.
여기서부터 나오는 내용은 개인의 궁금증 해소를 위한 기록임.
기본 old_space_size가 궁금해서 https://github.com/nodejs/node 의 소스코드를 뒤져보고 아래와 같은 결론을 얻었다.
node/deps/v8/src/heap/heap.cc 파일을 보면 기본 사이즈 지정하는 부분이 있다.
max_old_generation_size_(700ul * (kPointerSize / 4) * MB),
계산에 필요한 kPointerSize와 MB는 node/deps/v8/src/globals.h 에 정의되어 있다.
// 64bit에서 아래 값은 8 bytes
const int kPointerSize = sizeof(void*);
...
...
const int KB = 1024;
// MB 값은 1024 x 1024
const int MB = KB * KB;
계산해보면 대략 1.4G
700 x (8 / 4) x 1024 x 1024 = 1,460,006,400 = 1,400 MB
그리고 --max_old_space_size 값에 따라, 정말 죽는 시점이 달라지는지 실험을 해봤음.
heap memory 할당량을 뻥튀기 해야되는데, 단순히 object 키 값을 늘이거나 array.push를 많이 할 경우,
memory allocation 에러가 아니라 hash table 혹은 array max length 관련 에러로 먼저 죽어버리므로 코드를 잘 짜야함.
--max_old_space_size 값을 128, 256, 512, 1024, 2048, 4096, 8192로 바꿔가면서 top을 통한 RES 사이즈 체크와 동시에 파일에 process.memoryUsage의 반환값 중 rss, heapTotal, heapUsed 값을 기록하게 했다.
그러므로 파일에 기록된 값은 죽기 직전의 값이다.
--max_old_space_size 값 (MB) |
rss (MB) |
heapTotal (MB) |
heapUsed (MB) |
128 |
176 |
164 |
145 |
256 |
305 |
292 |
273 |
512 |
570 |
553 |
534 |
1024 |
1088 |
1061 |
1043 |
no option |
1462 |
1430 |
1405 |
2048 |
2138 |
2097 |
2066 |
4096 |
4199 |
30 |
6 |
8192 |
8474 |
91 |
67 |
rss 값을 보면 거의 설정한 수치 근처에서 죽는걸 확인할 수 있다.
근데 보다보면 굉장히 이상한 수치가 있는데,
--max_old_space_size 값을 4096, 8192로 설정했을 경우 heapTotal, heapUsed 값이 말도 안되게 작은 수치가 나오는 걸 볼 수 있다.
원인을 알아보기 위해 메모리를 할당하는 루프 구간에서 heapTotal이 드라마틱하게 줄어드는 시점의 직전 heapTotal 값을 출력하도록 코드를 추가하고 동일한 실험을 수행해서 아래와 같은 결과를 얻었다.
(단위는 byte)
--max_old_space_size를 4096으로 했을 때 heapTotal 값 4292467712 이후 사이즈가 확 줄어들었고, (1회 발생)
8192로 했을 때는 heapTotal 값이 4294861312 이후 사이즈가 확 줄었다가 서서히 증가하다가, 다시 4294345472 이후 사이즈가 확 줄어들었다. (2회 발생)
궁금해서 대충 뒤져보니 heapTotal, heapUsed 값을 반환하는 함수의 반환값 데이터 타입이 size_t라서 저런 이상한 값이 나오는 것 같다.
node/deps/v8/include/v8.h 에 보면 HeapStatistics 라는 class가 하나 있는데 total_heap_size()를 비롯하여 모든 method의 반환값 형태가 size_t 이다.
/**
* Collection of V8 heap information.
*
* Instances of this class can be passed to v8::V8::HeapStatistics to
* get heap statistics from V8.
*/
class V8_EXPORT HeapStatistics {
public:
HeapStatistics();
size_t total_heap_size() { return total_heap_size_; }
size_t total_heap_size_executable() { return total_heap_size_executable_; }
size_t total_physical_size() { return total_physical_size_; }
size_t total_available_size() { return total_available_size_; }
size_t used_heap_size() { return used_heap_size_; }
size_t heap_size_limit() { return heap_size_limit_; }
size_t malloced_memory() { return malloced_memory_; }
size_t peak_malloced_memory() { return peak_malloced_memory_; }
size_t does_zap_garbage() { return does_zap_garbage_; }
private:
size_t total_heap_size_;
size_t total_heap_size_executable_;
size_t total_physical_size_;
size_t total_available_size_;
size_t used_heap_size_;
size_t heap_size_limit_;
size_t malloced_memory_;
size_t peak_malloced_memory_;
bool does_zap_garbage_;
friend class V8;
friend class Isolate;
};
size_t가 unsigned integer라면 최대 수치는 4294967295 (0xffffffff) 이다.
위에서 출력된 3개의 값이 모두 4294967295 보다 작은 값이지만 근사한 값인 것으로 미루어 볼 때,
저 값을 넘어서면서 사이즈 값이 한바퀴 돌아서 처음부터 다시 올라가는 것이 아닐까 추측된다.
소스코드를 하나씩 따라가면서 확인하거나 컴파일해보고 내린 결론이 아니라 소스코드에서 대충 그럴듯한 내용을 검색해서 추측한 내용이라 정확한 원인인지는 모르겠지만.
.
'node.js' 카테고리의 다른 글
[node.js] cluster worker와 동기식(sync) 통신하기 (synchronous communication with cluster workers) (0) | 2019.06.26 |
---|---|
[node.js] self-signed certificate (0) | 2017.12.11 |
[node.js] 라즈베리파이에서 node.js로 mp3 재생하기 (0) | 2016.01.15 |
[node.js] 인터넷 연결상태 체크 (check internet connectivity) (0) | 2016.01.13 |
[node.js] http + cluster 구성시 worker graceful restart (no downtime) (0) | 2015.11.19 |