메모리 관리는 모든 프로그래밍 언어와 시스템에서 중요한 역할을 합니다. 다양한 언어에서 다양한 메모리 관리 방법들이 나왔습니다.
보통 4개의 구역으로 나뉘게 됩니다.
일반적인 메모리 관리 방식에서는 개발자가 힙 메모리를 할당하고 해제하는 것을 명시적으로 관리합니다. 이에 대한 애로사항이 많아 어떤 언어에서는 자동적으로 관리해주는 기능을 추가했습니다.
그 중 하나가 Node.JS입니다.
Node.js는 자바스크립트 런타임 환경으로, V8 엔진을 기반으로 작동합니다. 이 V8 엔진이 메모리 관리를 도와줍니다.
Node.js의 메모리 관리 방식은 주로 V8 엔진의 가비지 컬렉션(Garbage Collection) 메커니즘에 의해 관리됩니다.
process.memoryUsage() 함수를 통해 메모리 사용량을 모니터링할 수 있음| 요소 | 일반적인 메모리 관리 | Node.js 메모리 관리 |
|---|---|---|
| 메모리 관리 방식 | 수동(개발자가 명시적으로 관리) | 자동(V8 엔진의 가비지 컬렉션) |
| 스택과 힙 | 존재 | 존재 |
| 가비지 컬렉션 | 없음(수동으로 메모리 해제) | 있음(V8 엔진의 가비지 컬렉션) |
| 메모리 할당 및 해제 | 개발자가 직접 관리 | 자동으로 관리 |
| 메모리 제한 | 보통 제한 없음 | 기본 힙 메모리 제한 존재 |
| 메모리 사용 모니터링 | 개발자가 직접 구현 | process.memoryUsage()로 가능 |
이런 느낌이라고 보면 됩니다. STACK은 HEAP의 주소를 담은 배열입니다.
왜 인지 힙 압축하는 곳이 너무 깔끔해서 좋습니다.
/**
* 힙 영역에 할당된 타입들 중에서 스택에 포인터 변수가 없는 경우를 찾아서 해제하는 메소드
* @returns {Array} 힙 상태 배열
*/
garbageCollect() {
console.log('가비지 컬렉션 시작');
// 스택에서 참조하는 힙 주소들을 저장할 Set
const referencedAddresses = new Set();
// 스택을 순회하며 참조하는 힙 주소들을 수집
for (let stackAddr of this.STACK) {
if (this.STACK.length >= 0 && this.STACK.length < this.memorySize) {
// 힙 영역 주소 범위 체크
referencedAddresses.add(stackAddr);
}
}
// 힙을 순회하며 참조되지 않는 메모리 해제
for (let heapAddr = 0; heapAddr < this.HP; heapAddr++) {
if (this.HEAP[heapAddr] && !referencedAddresses.has(heapAddr)) {
const type = this.HEAP[heapAddr][0];
const size = Math.max(8, this.types[type]);
// 해당 메모리 블록 해제
for (let i = 0; i < size; i++) {
this.HEAP[heapAddr + i] = null;
}
console.log(
`주소 ${this.toHexAddress(heapAddr)}의 ${type} 타입 메모리 해제`,
);
}
}
// 힙 압축 (사용 중인 메모리를 앞으로 모으고 HP 조정)
let newHP = 0;
for (let heapAddr = 0; heapAddr < this.HP; heapAddr++) {
if (this.HEAP[heapAddr]) {
if (heapAddr !== newHP) {
this.HEAP[newHP] = this.HEAP[heapAddr];
this.HEAP[heapAddr] = null;
}
newHP++;
}
}
// HP 갱신
this.HP = newHP;
console.log('가비지 컬렉션 완료');
console.log(`새로운 HP: ${this.toHexAddress(this.HP)}`);
}