V8 메모리 관리
nodejs는 자바스크립트로 이루어져있고 자바스크립트는 V8엔진에 의해서 돌아간다
가비지 컬렉터의 작동하는 방식, 코드를 작성할 때 백그라운드에서 발생하는 일, 메모리가 해제되는 방식을 알아보자
메모리 영역
V8의 메모리공간을 Resident Set이라 부른다. 메모리 세그먼트는 아래와 같이 나뉜다

- Code : 실행될 코드들
- Stack : heap에 있는 object를 참조하는 포인터, 원시타입들이 있다.
- Heap : object, string, 클로저와 같은 레퍼런스 타입을 저장한다.
힙
- 새 영역(new space)
- 새 할당이 발생하는 영역, 대부분의 객체들이 여기에 있다. 잦은 GC가 발생하기 때문에 빠르게 GC될수 있도록 설계되었다.
- 메모리 크긴느 1~8MB 사이이며, Young Generation이라고도 부른다.
- 20% 정도가 old space로 장기화된다.
- 오래된 영역(old space) : GC가 두 번 발생할 동안 “New 영역”에서 살아남은 객체들이 이동하는 영역
- Old 포인터 영역: 살아남은 객체들을 가지며, 이 객체들은 다른 객체를 참조
- Old 데이터 영역: Old 포인터에 가지 않은 살아남은 변수들. 문자열, 박싱(boxing)된 숫자, 실수형(double)로 언박싱(unboxing)된 배열
- 라지 오브젝트 영역(Large-object space)
- 다른 space의 크기 제약보다 큰 크기를 가지는 객체들이 저장
- 각 객체들은 고유의 memory-mapped 영역을 가지며, Garbage collector에 의해 수집되지 않는다
- 코드 영역 (Code space)
- Just-In-Time 컴파일된 인스트럭션을 포함하는 코드 객체들이 저장된다.
- 실행 가능한 메모리를 가지는 유일한 space이다.
- Cell space, Property-cell space, Map space
- 각각 Cells, Property-Cells, Maps가 저장된다.
- 이 공간들에 위치한 객체들은 모두 그 크기와 타입이 같아서 GC가 쉽다.
스택
V8 프로세스마다 하나의 스택을 가진다
메모리 관리
프로그램이 제대로 작동하려면 메모리가 필요하고 그 때문에 메모리 관리가 필요하다
응용프로그램 수준에서 메모리 관리는 자동/수동으로 나뉘는데 대부분의 프로그램들의 자동 메모리 관리는 가비지 콜렉터가 맡고 있다
수동으로 메모리를 관리는 C언어등에서 malloc, free 등을 통해 관리하는 것이다
수동 메모리 관리에서 주로 발생할 수 있는 버그들
- 사용한 메모리 공간을 반환하지 않으면 메모리 누수가 발생한다
- 개체된 객체의 포인터가 재사용되면 중요한 정보를 읽을 때 심각한 보안 문제가 발생할 수 있다
Node.js에는 가비지 수집기가 포함되어 있으므로 메모리 할당을 수동으로 관리할 필요가 없다
가비지 컬렉터의 개념
참조를 잃어 더이상 사용할 수 없는 객체들을 가비지 컬렉터가 자동으로 수집해서 메모리를 반환시켜준다
1
2
3
4
5
6
7
8
9
10
11
12
function Engine (power) {
this.power = power
}
function Car (opts) {
this.name = opts.name
this.engine = new Engine(opts.power)
}
let LightningMcQueen = new Car({name: 'Lightning McQueen', power: 900})
let SallyCarrera = new Car({name: 'Sally Carrera', power: 500})
let Mater = new Car({name: 'Mater', power: 100})

Mater = undefined 코드를 추가하면 Mater 객체가 참조를 잃고 더이상 접근할 수 있는 방법이 없기 때문에 가비지 콜렉터에 의해 수집당한다

가비지 콜렉터

이런 모양을 가지고 있다
New Space : From-space, To-space
Old Space : Pointer Space, Data Space
- 대부분의 객체들은 그림 1번의 From-space에서 생성된다
- From-spaced에서 객체가 가득차면 To-space에서 가비지 콜렉터를 실행하고 살아남은 객체들을 그림 2번의 To-space로 옮긴다
- 1~2번의 과정으로 To-space마저 가득차면 To-space에서 가비지 콜렉터를 실행하고 객체를 그림 3번의 Old Space로 옮긴다
- Old Space까지 가득 찼다고 판단하면 V8은 메이저 가비지 콜렉터를 돌린다
메이저 가비지 콜렉터의 알고리즘은 다음과 같은 단계를 거친다
마킹
현재 어떤 객체가 사용중인지 어떤 객체가 참조가 끊겨서 사용할 수 없는지를 판단한다 사용 중이거나 가능한 객체를 활성 상태로 마킹한다
스위핑
가비지 콜렉터가 힙을 순회하면서 1단계에서 마킹하지 않은 객체들의 메모리 주소를 기록한다 이제 이 객체들의 메모리 주소에는 다른 객체들이 들어갈 수 있다
압축
2단계가 끝난 후 모든 활성 객체들이 압축되어 메모리 공간의 효율을 높여 새 객체들의 할당 성능을 증가시킨다
메이저 GC는 앱을 잠시 멈추게하는데 이를 피하기 위해 다음과 같은 기술을 사용한다
- 인크리멘탈 GC(Incremental GC): GC는 여러 개의 인크리멘탈 단계로 수행된다.
- 동시 마킹(Concurrent marking): 마킹은 자바스크립트 메인 스레드에 영향을 주지 않고 다중 헬프 스레드를 사용해 동시에 수행된다. Write barrier는 헬퍼들이 동시에 마킹하는 동안 자바스크립트가 생성한 객체 간 참조를 추적하는 데 사용된다.
- 동시 스위핑/압축(Concurrent sweeping/compacting): 스위핑과 압축은 자바스크립트 메인 스레드에 영향을 주지 않고 헬퍼 스레드에서 동시에 수행된다.
- 레이지 스위핑(Lazy sweeping): 레이지 스위핑은 메모리가 필요할 때까지 페이지에서 가비지 삭제를 지연시킨다.