v8엔진의 메모리 구조
v8엔진의 가비지컬렉션을 살펴보기 전, v8엔진의 메모리 구조에 대해 알아보겠습니다.
프로그램을 실행하면 메모리의 Resident Set이라는 빈 공간이 할당됩니다. 이 Resident Set은 스택과 힙 영역으로 구성됩니다.
자바스크립트의 경우 싱글스레드이기 때문에 스택(Stack) 메모리를 하나만 가집니다. 하지만 GC에서 중요하게 봐야할 것은 바로 힙(Heap) 메모리입니다. 왜냐하면, 스택의 경우 함수 호출이 끝난 후 OS에 의해 자동으로 정리되지만, 힙 메모리는 그렇지 않기 때문입니다.
따라서 힙에 있는 쓸모없는 메모리가 해제되지 않을 경우 메모리 누수가 발생하여 프로그램 속도는 점점 느려집니다.
v8엔진과 가비지 컬렉션(GC) ****
v8엔진은 힙 영역을 generations라고 불리는 New, Old space로 나눴습니다. 이때 New space는 2개의 semi space로 또 나뉩니다.
객체는 제일 처음 New space의 첫번째 semi space에 할당됩니다. 만약 GC로부터 한 번 생존시 다른 semi space로 이동합니다. 그리고 생존한 객체들이 또 한 번 GC로부터 생존하면 Old space로 이동합니다.
가비지 컬렉션은 New space에서의 마이너 GC와 Old space에서의 메이저 GC로 각각 다른 방식으로 동작합니다.
이때 Heap영역은 처음 설계될 때 Weak Generational Hypothesis라는 가설에 기반하여 설계되었습니다. 이는 대부분의 경우 새로운 객체가 오래된 객체보다 쓸모없어질 가능성이 높다는 것입니다. 오래된 객체가 쓸모없어질 가능성이 낮은데 GC가 모든 객체를 매번 검사하는 것은 비효율적입니다.
따라서 객체의 특성에 맞춰 힙 영역을 크게 New space, Old space로 분류하여 새로운 객체는 New space에서, 오래된 객체는 Old space에서 각 영역에 최적화된 GC들로 관리합니다.
마이너 GC는 객체들의 생명주기가 짧은 New space에서 빠르게 가비지 컬렉션을 하고, 메이저 GC는 메모리 사이즈가 큰 Old space에서 가비지 컬렉션을 합니다.
마이너 GC(Scavenger)
먼저 마이너 GC에 대해 자세히 살펴보겠습니다. New space의 garbage를 수집해주는 마이너 GC입니다. New space에 있는 대부분의 객체들은 수집되기를 즉, 메모리에서 해제되기를 원합니다.
마이너 GC에서 살아남은 객체들은 항상 새로운 곳으로 eavacuation(대피)합니다. 즉 New space의 2개의 semi space에서 비어있는 semi space로 이동합니다. 이러한 이동 과정을 위해 New space의 2개의 semi space 중 하나는 항상 비어있습니다. 이 비어있는 영역은 To space, 객체들이 머무르는 영역은 From space 즉, 원래 객체들이 살아남으면 기존 semi space에서(From) 비어있는 semi space로(To) 이동한다는 의미입니다.
이러한 과정은 메모리 단편화를 주기적으로 방지하며, 객체는 새로운 메모리 주소값으로 포인터가 갱신됩니다.
From space에서 To space로 살아남은 객체들의 대피가 완료되면, From space에 남아있는 더 이상 사용되지 않는 객체들은 가비지 컬렉팅 되어 사라집니다. 그렇게 비워진 From space는 다시 To space로서의 역할을 맡게되고, To space는 From space의 역할을 맡게 됩니다.
그렇다면 New space에서 Old space로의 이동은 어떻게 이루어질까요? 🤔
새로운 객체가 할당된다고 가정하면, 먼저 New space의 From space의 semi space의