🔍 검색 키워드: GitHub Actions JavaScript heap out of memory, FATAL ERROR Reached heap limit Allocation failed, max-old-space-size GitHub Actions, NODE_OPTIONS 힙 메모리 늘리기, Node.js heap out of memory 빌드, GitHub Actions 빌드 메모리 부족
요약: "FATAL ERROR: Reached heap limit Allocation failed - JavaScript heap out of memory"는 Node.js 프로세스가 V8 힙 상한을 넘겨 메모리를 더 확보하지 못해 강제 종료될 때 나옵니다. workflow의 env에 NODE_OPTIONS=--max-old-space-size=값을 지정해 상한을 올리거나, 빌드 자체의 메모리 사용량(소스맵, 병렬 작업 등)을 줄여서 해결합니다.
JavaScript heap out of memory는 왜 발생하나요?
Node.js는 V8 엔진의 힙(old space) 메모리에 상한을 둡니다. 빌드 도구(webpack, tsc, 번들러 등)가 큰 프로젝트를 처리하다가 이 상한을 넘으면 가비지 컬렉션으로도 공간을 확보하지 못해 프로세스를 중단합니다. 로컬에서는 괜찮다가 GitHub Actions에서만 실패하는 경우는 보통 러너 환경과 Node 버전, 프로젝트 크기가 달라서입니다.
직접 재현한 결과
아래 출력은 로컬에서 직접 실행한 결과이며 GitHub Actions 러너에서 실행한 것은 아닙니다. 빌드 도구 대신, 배열을 계속 늘리는 간단한 스크립트로 힙 상한을 넘기는 상황을 만들었습니다. 빠르게 재현하려고 상한을 64MB로 낮췄습니다.
- 환경: macOS 27.0.1, Node.js v20.20.2, RAM 24GB
// leak.js
const a = [];
while (true) a.push(new Array(1e5).fill('x'));$ node --max-old-space-size=64 leak.js
... Mark-Compact 64.2 (98.1) -> 64.2 (98.1) MB, ...
allocation failure; scavenge might not succeed
FATAL ERROR: Reached heap limit Allocation failed -
JavaScript heap out of memory
종료 코드: 134 (Abort trap: 6)가비지 컬렉션(Mark-Compact)을 해도 사용량이 줄지 않다가 상한에 닿아 프로세스가 중단됐습니다. 종료 코드는 134였습니다.
힙 상한은 어떻게 확인하고 바꾸나요?
v8 모듈의 getHeapStatistics()로 현재 상한(heap_size_limit)을 확인할 수 있습니다. 직접 확인한 결과는 다음과 같습니다.
$ node -e "console.log(require('v8').getHeapStatistics().heap_size_limit/1024/1024)"
4144 # 이 컴퓨터(RAM 24GB, Node 20)의 기본값(MB)
$ NODE_OPTIONS=--max-old-space-size=2048 node -e "..."
2096 # NODE_OPTIONS로 지정한 값이 적용됨두 가지를 확인했습니다. 첫째, NODE_OPTIONS로 지정한 값이 실제로 적용됩니다. 둘째, 이 컴퓨터는 기본 상한이 이미 약 4GB라서 --max-old-space-size=4096을 줘도 값이 4144로 같았습니다. 기본 상한은 Node 버전과 시스템 메모리에 따라 달라지므로, 숫자를 무작정 키우기 전에 현재 상한을 먼저 확인하세요. 러너의 기본 상한과 사용 가능한 메모리는 직접 확인하지 못했으며 GitHub 공식 문서의 러너 사양을 확인해야 합니다.
어떻게 해결하나요?
1) NODE_OPTIONS로 힙 상한 올리기
jobs:
build:
runs-on: ubuntu-latest
env:
NODE_OPTIONS: --max-old-space-size=4096
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
- run: npm ci
- run: npm run build위 workflow는 일반적인 형태의 예시이며 실제 러너로 실행해 확인하지는 않았습니다. 값은 러너의 실제 메모리보다 크게 잡으면 안 됩니다. 상한이 물리 메모리를 넘으면 힙 오류 대신 운영체제가 프로세스를 강제 종료할 수 있습니다.
2) 빌드 스크립트에만 적용하기
{
"scripts": {
"build": "NODE_OPTIONS=--max-old-space-size=4096 vite build"
}
}모든 Node 프로세스가 아니라 빌드 한 곳에만 적용하고 싶을 때 씁니다. Windows 러너를 쓴다면 cross-env 같은 도구가 필요합니다.
3) 메모리 사용량 자체를 줄이기
- 프로덕션 소스맵 생성을 끄거나 줄입니다. 소스맵은 빌드 메모리를 많이 쓰는 대표적인 옵션입니다.
- 병렬 처리 수를 줄입니다(예: 번들러의 worker 수, 테스트의 --maxWorkers).
- 타입 검사와 번들을 한 프로세스에서 같이 돌리고 있다면 단계를 나눕니다.
- 한 번에 처리하는 파일이 너무 크지 않은지 확인합니다.
주의할 점
- 상한을 올리는 것은 증상을 미루는 방법일 수 있습니다. 메모리 누수가 원인이면 값을 키워도 시간이 조금 늦어질 뿐 다시 실패합니다.
- GitHub Actions 로그에서 같은 단계가 "Killed"나 종료 코드 137로 끝나면 힙 오류가 아니라 운영체제 수준의 메모리 부족일 가능성이 있습니다. 이 경우는 위 재현과는 다른 증상이며 직접 재현하지는 않았습니다.
- Node 버전이 바뀌면 기본 힙 상한이 달라질 수 있으므로, setup-node의 node-version을 고정하세요.
정리표
| 증상 | 원인 | 해결 |
|---|---|---|
| FATAL ERROR ... heap out of memory (종료 코드 134) | V8 힙 상한 초과 | NODE_OPTIONS=--max-old-space-size 지정 |
| 상한을 올려도 계속 실패 | 메모리 누수 또는 과도한 사용 | 소스맵·병렬 수·작업 분할 점검 |
| Killed / 종료 코드 137 | OS 수준 메모리 부족(문서 기준) | 상한을 낮추거나 러너 사양 변경 |
자주 묻는 질문(FAQ)
--max-old-space-size는 단위가 무엇인가요?
메가바이트(MB)입니다. 직접 확인한 결과, 2048을 지정하면 heap_size_limit가 약 2096MB로 표시됩니다.
NODE_OPTIONS는 어디에 넣어야 하나요?
job 또는 step의 env에 넣거나 package.json의 스크립트 앞에 붙입니다. 모든 Node 프로세스에 적용되므로 필요한 범위로만 지정하는 것이 좋습니다.
로컬에서는 되는데 GitHub Actions에서만 실패하는 이유는 무엇인가요?
Node 버전, 시스템 메모리, 프로젝트 의존성 상태가 다를 수 있기 때문입니다. 힙 상한을 확인하는 명령을 workflow에 한 번 출력해 비교해 보세요.
상한을 크게 올리면 항상 해결되나요?
아닙니다. 누수가 원인이면 다시 실패하고, 값이 물리 메모리보다 크면 OS가 프로세스를 종료할 수 있습니다.
참고 문서: Node.js CLI options · GitHub-hosted runners
최종 업데이트: 2026-10-08 · 작성: 내멋스레 사는 이야기
댓글 없음:
댓글 쓰기