Node.js API에 능숙하면 빠르게 작업을 진행할 수 있지만 Node.js 프로그램의 메모리 사용량을 깊이 이해하면 더 많은 작업을 수행할 수 있습니다.
process.memoryUsage()를 사용하여 메모리 사용량을 살펴보고 매초 업데이트하면서 시작하겠습니다.
setInterval(() => { console.log('Memory Usage:', process.memoryUsage()); }, 1000);
출력이 바이트 단위이므로 사용자에게 친숙하지 않습니다. 메모리 사용량을 MB:
로 형식화하여 정리하겠습니다.
function formatMemoryUsageInMB(memUsage) { return { rss: convertToMB(memUsage.rss), heapTotal: convertToMB(memUsage.heapTotal), heapUsed: convertToMB(memUsage.heapUsed), external: convertToMB(memUsage.external) }; } const convertToMB = value => { return (value / 1024 / 1024).toFixed(2) + ' MB'; }; const logInterval = setInterval(() => { const memoryUsageMB = formatMemoryUsageInMB(process.memoryUsage()); console.log(`Memory Usage (MB):`, memoryUsageMB); }, 1000);
이제 매초 다음 출력을 얻을 수 있습니다.
Memory Usage (MB): { rss: '30.96 MB', // The actual OS memory used by the entire program, including code, data, shared libraries, etc. heapTotal: '6.13 MB', // The memory area occupied by JS objects, arrays, etc., dynamically allocated by Node.js // V8 divides the heap into young and old generations for different garbage collection strategies heapUsed: '5.17 MB', external: '0.39 MB' } Memory Usage (MB): { rss: '31.36 MB', heapTotal: '6.13 MB', heapUsed: '5.23 MB', external: '0.41 MB' }
V8 엔진의 메모리 사용량은 OS의 메모리 관리 및 리소스 할당 정책뿐만 아니라 자체 설정에 의해서도 제한된다는 사실은 모두가 알고 있습니다.
os.freemem()을 사용하면 OS에 얼마나 많은 여유 메모리가 있는지 확인할 수 있지만 그렇다고 Node.js 프로그램이 모든 메모리를 확보할 수 있다는 의미는 아닙니다.
console.log('Free memory:', os.freemem());
64비트 시스템의 경우 Node.js V8의 기본 최대 이전 공간 크기는 약 1.4GB입니다. 즉, OS에 사용 가능한 메모리가 더 많아도 V8은 자동으로 이 한도 이상을 사용하지 않습니다.
팁: 이 제한은 Node.js를 시작할 때 환경 변수를 설정하거나 매개변수를 지정하여 변경할 수 있습니다. 예를 들어, V8이 더 큰 힙을 사용하도록 하려면 --max-old-space-size 옵션을 사용할 수 있습니다:
node --max-old-space-size=4096 your_script.js
이 값은 실제 상황과 시나리오에 따라 설정되어야 합니다. 예를 들어, 메모리가 많고 독립 실행형으로 배포된 머신이 있고 분산 방식으로 배포된 소형 메모리 머신이 많은 경우 이 값에 대한 설정은 확실히 다릅니다.
메모리가 오버플로될 때까지 배열에 데이터를 무한정 채워서 테스트를 실행하고 언제 발생하는지 살펴보겠습니다.
const array = []; while (true) { for (let i = 0; i < 100000; i++) { array.push(i); } const memoryUsageMB = formatMemoryUsageInMB(process.memoryUsage()); console.log(`Memory Usage (MB):`, memoryUsageMB); }
프로그램을 직접 실행하면 나오는 내용입니다. 잠시 동안 데이터를 추가한 후 프로그램이 충돌합니다.
Memory Usage (MB): { rss: '2283.64 MB', heapTotal: '2279.48 MB', heapUsed: '2248.73 MB', external: '0.40 MB' } Memory Usage (MB): { rss: '2283.64 MB', heapTotal: '2279.48 MB', heapUsed: '2248.74 MB', external: '0.40 MB' } # # Fatal error in , line 0 # Fatal JavaScript invalid size error 169220804 # # # #FailureMessage Object: 0x7ff7b0ef8070
혼란스러우신가요? 1.4G 한도 아닌가요? 2G 이상을 사용하는 이유는 무엇입니까? 실제로 Node.js의 1.4GB 제한은 V8 엔진의 역사적 제한이며 초기 V8 버전 및 특정 구성에 적용됩니다. 최신 Node.js 및 V8에서 Node.js는 시스템 리소스를 기반으로 메모리 사용량을 자동으로 조정합니다. 어떤 경우에는 특히 대규모 데이터 세트를 처리하거나 메모리 집약적인 작업을 실행할 때 1.4GB보다 훨씬 더 많은 공간을 사용할 수 있습니다.
메모리 제한을 512M으로 설정하면 RSS가 996MB 정도에 도달하면 오버플로됩니다.
Memory Usage (MB): { rss: '996.22 MB', heapTotal: '993.22 MB', heapUsed: '962.08 MB', external: '0.40 MB' } Memory Usage (MB): { rss: '996.23 MB', heapTotal: '993.22 MB', heapUsed: '962.09 MB', external: '0.40 MB' } <--- Last few GCs ---> [22540:0x7fd27684d000] 1680 ms: Mark-sweep 643.0 (674.4) -> 386.8 (419.4) MB, 172.2 / 0.0 ms (average mu = 0.708, current mu = 0.668) allocation failure; scavenge might not succeed [22540:0x7fd27684d000] 2448 ms: Mark-sweep 962.1 (993.2) -> 578.1 (610.7) MB, 240.7 / 0.0 ms (average mu = 0.695, current mu = 0.687) allocation failure; scavenge might not succeed <--- JS stacktrace ---> FATAL ERROR: Reached heap limit Allocation failed - JavaScript heap out of memory
좀 더 정확하게 정리하면 Node.js의 메모리 제한은 힙 메모리 제한을 의미하는데, 이는 V8에서 할당한 JS 객체, 배열 등이 점유할 수 있는 최대 메모리입니다.
힙 메모리 크기에 따라 Node.js 프로세스가 차지할 수 있는 메모리 양이 결정되나요? 아니요! 계속 읽어보세요.
테스트에서 프로그램이 충돌하기 전에 어레이가 2GB를 약간 넘는 용량만 저장할 수 있다는 것을 확인했습니다. 그럼 3GB 파일이 있으면 Node.js 메모리에 한꺼번에 넣을 수는 없을까요?
할 수 있습니다!
우리는 process.memoryUsage()를 통해 Node.js 프로세스에 의해 점유되지만 V8에 의해 할당되지 않는 외부 메모리를 보았습니다. 3GB 파일을 거기에 넣으면 메모리 제한이 없습니다. 어떻게? 버퍼를 사용할 수 있습니다. Buffer는 JS 객체와 데이터가 아닌 C를 이용해 메모리를 할당하는 Node.js의 C 확장 모듈입니다.
데모는 다음과 같습니다.
setInterval(() => { console.log('Memory Usage:', process.memoryUsage()); }, 1000);
3GB의 메모리를 할당하더라도 우리 프로그램은 원활하게 실행되고 있으며, 이 외부 메모리는 Node.js에 의한 제한이 아니라 운영 체제의 할당 메모리 제한에 의해 제한되기 때문에 Node.js 프로그램이 5GB 이상의 메모리를 차지했습니다. 스레드로 전환합니다(그래서 너무 무리해서 사용할 수는 없으며 버퍼에도 메모리가 부족할 수 있습니다. 핵심은 Streams로 대규모 데이터를 처리하는 것입니다).
Node.js에서 Buffer 개체의 수명 주기는 JavaScript 개체에 연결되어 있습니다. Buffer 객체에 대한 JavaScript 참조가 제거되면 V8 가비지 수집기는 객체를 재활용 가능으로 표시하지만 Buffer 객체의 기본 메모리는 즉시 해제되지 않습니다. 일반적으로 C 확장의 소멸자가 호출되면(예: Node.js의 가비지 수집 프로세스 중에) 메모리의 이 부분이 해제됩니다. 그러나 이 프로세스는 V8의 가비지 컬렉션과 완전히 동기화되지 않을 수 있습니다.
function formatMemoryUsageInMB(memUsage) { return { rss: convertToMB(memUsage.rss), heapTotal: convertToMB(memUsage.heapTotal), heapUsed: convertToMB(memUsage.heapUsed), external: convertToMB(memUsage.external) }; } const convertToMB = value => { return (value / 1024 / 1024).toFixed(2) + ' MB'; }; const logInterval = setInterval(() => { const memoryUsageMB = formatMemoryUsageInMB(process.memoryUsage()); console.log(`Memory Usage (MB):`, memoryUsageMB); }, 1000);
요약하면 Node.js 메모리 사용량은 JS 힙 메모리 사용량(V8의 가비지 컬렉션에 의해 결정됨) C에 의한 메모리 할당으로 구성됩니다.
세대별 가비지 수집 전략은 현대 프로그래밍 언어 구현에서 매우 널리 퍼져 있습니다! 세대별 가비지 수집과 같은 유사한 전략은 Ruby, .NET 및 Java에서 찾을 수 있습니다. 가비지 수집이 발생하면 종종 "세계를 중지"하는 상황이 발생하여 프로그램 성능에 필연적으로 영향을 미칩니다. 하지만 이 디자인은 성능 최적화를 염두에 두고 고안되었습니다.
메모리 할당은 From 내에서 이루어집니다. 가비지 수집 중에 From의 활성 개체가 검사되어 To로 복사된 후 비활성 개체가 해제됩니다. 후속 수집 라운드에서는 To의 라이브 개체가 From으로 복제되고, 이 시점에서 To는 From으로 변형되고 그 반대의 경우도 마찬가지입니다. 각 가비지 수집 주기마다 From과 To가 교체됩니다. 이 알고리즘은 복사 프로세스 중에 활성 개체만 복제하므로 메모리 조각 생성을 방지합니다.
그렇다면 변수의 활성 여부는 어떻게 결정됩니까? 도달가능성 분석이 시작됩니다. 예를 들어 다음 개체를 고려하십시오.
접근성 분석의 맥락에서:
물론 참조 카운팅이 보조 수단으로 사용될 수 있습니다. 그럼에도 불구하고 순환 참조가 있으면 객체의 실제 활성 상태를 정확하게 확인하지 못합니다.
구세대 메모리에서 객체는 일반적으로 덜 활동적입니다. 하지만 Old Generation 메모리가 가득 차면 Mark-Sweep 알고리즘을 통해 Old Generation 메모리(Major GC) 정리를 시작합니다.
Mark-Sweep 알고리즘은 마킹과 스위핑의 두 단계로 구성됩니다. 마킹 단계에서 V8 엔진은 힙의 모든 객체를 탐색하고 활성 객체에 태그를 지정합니다. 스위핑 단계에서는 표시되지 않은 개체만 지워집니다. 이 알고리즘의 장점은 Old Generation에서 죽은 객체의 비율이 상대적으로 적기 때문에 스위핑 단계에서 상대적으로 적은 시간을 소비한다는 것입니다. 하지만 압축하지 않고 클리어만 하기 때문에 불연속적인 메모리 공간이 생길 수 있어 큰 객체에 메모리 할당이 불편하다는 단점이 있습니다.
이러한 단점으로 인해 메모리 조각화가 발생하고 Mark-Compact라는 또 다른 알고리즘을 사용해야 합니다. 이 알고리즘은 모든 활성 개체를 한쪽 끝으로 이동한 다음 경계 오른쪽의 유효하지 않은 메모리 공간을 한 번에 제거하여 완전하고 연속적인 사용 가능한 메모리 공간을 얻습니다. 많은 수의 라이브 개체를 이동하는 데 더 많은 시간이 소요되지만 Mark-Sweep 알고리즘으로 인해 발생할 수 있는 메모리 조각화 문제를 해결합니다.
이 게시물이 유익하셨다면 좋아요를 눌러주세요. :디
위 내용은 Node.js의 메모리 제한은 정확히 무엇입니까?의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!