[JavaScript] 가비지 컬렉션(GC)

한호수 (The Lake)·2023년 4월 9일
71
post-thumbnail

면접스터디 도중 가비지 컬렉션(GC)에 관한 말이 나왔는데 딥하게 답해주는 동료를 보고 GC에 관심이 생겨서 좀더 찾아보게 되었다. 많이 어려웠는데 카카오블로그를 통해서 알게된 내용을 간단히만 포스팅해본다.

가비지 컬렉션이란?

C언어와 같은 저수준 언어에서는 수동으로 메모리를 해제해주어야 하지만 대다수의 고수준 언어에서는 가비지 컬렉터(Garbage Collection)를 통한 자동 메모리 회수기능을 제공하고 있으며 이 행위를 가비지 컬렉션이라고 한다. 그렇다면 자바스크립트 v8엔진에서는 어떻게 메모리를 회수할까?

안 돼! 그건 내가 쓰고 있는 객체야!!

V8 엔진의 메모리 구조

V8 엔진의 Memory

프로그램을 실행하면 운영체제로 부터 메모리에 Resident Set라는 빈 공간을 할당 받는데 이는 현재 실행 중인 프로세스가 실제로 사용하고 있는 메모리의 양을 말한다. 즉, 물리적 메모리에서 현재 사용 중인 부분이다.

Stack

V8 엔진의 Memory는 크게 Stack과 Heap Memory로 나눌 수 있는데 Stack에는 보통 작은 크기의 원시값(Number, Boolean, Undefined, Null)들이 저장된다. 또한 식별자, 함수의 매개변수, 참조 타입들의 주소값 , 실행 컨텍스트 등도 Stack에 저장된다.

Heap Memory

Heap 메모리에는 어떤 데이터가 저장될까? Heap에는 큰 크기의 원시값 (문자열, 배열 등 참조형 데이터)가 저장된다. 힙은 동적으로 할당된 메모리 영역으로서, 객체가 프로그램에서 생성될 때마다 메모리를 할당하고 더 이상 필요하지 않을 때 가비지 컬렉터를 통해서 메모리를 해제하기 때문이다.

그렇다! 내가 알던 가비지 컬렉터는 정확히 Heap Memory에서 동작하는 것이었다.

Garbage Collector

카카오블로그 권현빈(vinny)님의 포스트 이미지

v8의 가비지 컬렉터 (이하 GC)는 The Generational Hypothesis 가설에 따라 최적화가 되어 있다. 이 가설은 새로운 객체가 오래된 객체보다 쓸모없어질 확률이 높다는 이야기이다.

v8의 GC는 이 가설에 따라 Heap Memory에 New Spaceold Space의 공간을 만들고 새로운 객체가 생성되면 New Space에 검사하고 두번의 검사에 살아남은 객체들은 Old Space로 이동시켜 비교적 적은 횟수로 검사한다.

이 중 New Space1~8MB 정도의 공간으로 마이너 GC 알고리즘이 검사를 담당하게 되는데 그 과정은 아래와 같다.

마이너 GC(Scavenger)

힙 메모리 속 New Space에서 벌어지는 일들

New Space는 Semi Space로 나뉘어져 있지만 각각의 Semi Space는 From-SpaceTo-Space로 불리며 마이너 GC가 검사를 할때마다 살아남은 객체들은 To-Space로 대피하게 되고 From-Space 에 남은 객체들은 모두 사라지게 된다.

이때 대피를 할때마다 메모리 단편화를 막기위해 살아남은 객체들은 연속적으로 붙어있는 메모리로 이동하게 된다.

최악의 경우 모든 개체가 청소에서 살아남을 수 있으며 모든 개체를 복사해야 한다.

New Space 속 Semi Space들은 각각 전체 공간의 절반씩 차지하며 대피소로 사용되는 To-Space는 검사전에는 항상 비어있다. 이 말은 From-Space에서 살아남는 객체들이 많을수록 To-Space에는 공간이 점점 부족해지는 것을 의미한다.

절반하면 떠오르는 그리운 한 인물

그래서 넓은 Old Space로 이동시켜서 검사하는 것이다. 마이너 GC의 이런 과정은 Cheney’s algorithm을 적용한것이다.

메이저 GC(Full Mark-Compact)

Major GC의 동작

Old Space를 검사하는 메이저 GC 는 Mark-Sweep-Compact 알고리즘과 Tri-color 알고리즘을 사용하며 다음과 같이 세단계로 나뉜다.

Major GC는 마킹, 스위핑, 압축의 세 단계로 이루어진다.

Marking(식별)

  • GC가 어떤 객체가 사용 중인지와 사용되지 않는지를 식별하는 단계
  • 사용 중이거나 GC 루트(Stack pointers)에서 재귀적으로 도달 가능한 객체는 살아있는 것으로 표시
  • 사실상 heap을 directed graph로 간주한 깊이 우선 검색

Sweeping(회수)

  • GC가 heap을 순회하면서 살아남은 객체가 표시되지 않은 메모리 주소를 확인
  • 해당 공간은 이제 Free-List에서 비어 있음으로 표시되고 다른 객체를 저장하는 데 사용할 수 있다.

Compacting(정리)

  • Sweeping 이후, 필요한 경우 모든 살아남은 객체는 함께 이동된다.
  • 이를 통해 메모리 단편화를 감소시키고 새로운 객체에 대한 메모리 할당 성능을 높일 수 있다.

마이너 GC와 메이저 GC가 동작할때마다 프로그램이 멈추게 되는데 이를 stop-the-world 라고 부른다. 이를 해결하기 위해 구글은 Orinoco 프로젝트를 진행하였고 , 최신 GC는 메인 쓰레드 블록킹 시간을 줄이기 위해 많은 기술이 추가되었다.

Reference
https://fe-developers.kakaoent.com/2022/220519-garbage-collection/
https://v8.dev/blog/trash-talk
https://v8.dev/blog/concurrent-marking
https://medium.com/hcleedev/web-javascript%EC%9D%98-garbage-collection-v8-%EC%97%94%EC%A7%84-9409c5be917c
https://deepu.tech/memory-management-in-v8/

profile
항상 근거를 찾는 사람이 되자

1개의 댓글

comment-user-thumbnail
2023년 4월 13일

가비지 컬렉터에 대해 명쾌한 글이네요 면접 때 써먹어볼게요 :)

답글 달기