본문 바로가기
javascript

V8 Engine memory management

by csue 2021. 7. 23.

before reading

이 포스팅은 V8 Engine 의 Memory management 에 관한 포스팅을 읽고 자기 학습 차원에서 요약한 것이므로 관련 레퍼런스들에 대한 링크를 서문에 남긴다.

 

🚀 Demystifying memory management in modern programming languages

Let us take a look at how modern programming languages manage memory.

dev.to

 

 

🚀 Visualizing memory management in V8 Engine (JavaScript, NodeJS, Deno, WebAssembly)

Let us take a look at how the V8 engine for JavaScript & WebAssembly manages memory for Browsers and NodeJS.

dev.to

 

 

자바스크립트와 V8 엔진의 메모리 관리 프로세스

Table of Contents

medium.com

 

V8 엔진(자바스크립트, NodeJS, Deno, WebAssembly) 내부의 메모리 관리 시각화하기 | TOAST UI :: Make Your Web

트위터 계정을 팔로우하고 글에서 개선할 내용이 있다면 알려달라. 이번 연재물에서는 메모리 관리 개념을 이해하고 현대 프로그래밍 언어에서 사용하는 메모리 관리 방법에 대해 자세히 살펴

ui.toast.com

What is Memory management

Memory management 는 소프트웨어 애플리케이션이 computer memory 에 접근하는 방식을 통제하고 조정하는 것을 의미한다. 소프트웨어가 바이트 코드/런타임 시스템을 로드하거나 실행되는 프로그램이 사용하는 데이터와 데이터 구조를 임시로 저장하는 등 컴퓨터의 운영체제에서 실행될 때, 컴퓨터의 Random access memory = RAM 의 접근을 필요로 한다. RAM 은 임의의 영역에 접근하여 읽고 쓰기가 가능한 컴퓨터의 주기억 장치로 휘발성 메모리이다. 일반적으로 메모리는 스택stack과 힙heap 으로 분류된다.

  • 스택stack
    후입선출 LIFO(Last In First Out)
    정적 메모리 할당 시에 사용된다. 저장되는 데이터의 크기가 유한하고 정적이다. 데이터 서치를 하지 않기 때문에 빠른 속도로 데이터를 저장하고 회수할 수 있는 것이 최대 장점이다.
    함수의 호출 정보 또한 스택 프레임으로 저장 된다. 각각의 프레임은 함수 호출 시 필요로 하는 데이터를 저장한 한 블록의 공간을 의미한다. 해당 함수가 새 변수를 생성하면 그 데이터는 스택 맨 위의 블록에 저장되고, 함수 호출이 종료되면 스택 맨 위의 블록이 제거된다. 멀티 스레딩이 가능한 애플리케이션은 스레드의 수만큼 스택을 여러 개 가질 수 있다. 일반적으로 스택에 저장되는 데이터 종류에는 지역변수, 포인터, 함수 프레임 등이 있다. 스택에 저장될 수 있는 데이터의 사이즈에는 상한이 있으며, 해당 상한선을 초과하는 경우 StackOverflow 에러가 발생한다.
  • 힙heap
    힙은 동적 메모리 할당 시에 사용된다. 포인터를 사용하여 데이터의 위치를 저장한다. 데이터를 처리하고 접근하는 속도는 느리지만 더 큰 용량의 데이터를 저장할 수 있으나 할당코자 하는 메모리의 사이즈가 동적으로 변동 가능하기에 컴파일 타임에 메모리 사이즈를 정확하게 예측할 수 없으므로 메모리 관리가 복잡해진다. 힙 메모리는 스레드 간 공유가 되며, 일반적으로 저장되는 데이터의 종류로는 전역변수, reference types(objects, str, map) 등이 있다. 일반적으로 힙 메모리 용량에 제한은 따로 없으나 할당된 힙 공간을 초과하는 메모리를 사용하려고 하면 OutOfMemory 에러가 발생한다.

RAM 의 용량은 무한하지 않으므로, 메모리를 더 이상 사용하지 않을 시에는 할당 해제해주어야 한다.

메모리의 라이프 사이클은 다음과 같다.

 

1. 할당 : 운영 체제가 메모리를 할당한다. 자바스크립트의 경우 자동으로 할당한다.

2. 사용 : 메모리가 할당된 변수가 실질적으로 사용된다.

3. 해제 : 프로그램에서 필요하지 않은 메모리를 반환하여 다시 사용할 수 있게 해준다.

GC garbage collection

가비지 컬렉션은 힙 메모리에 할당되어 있는 객체, 문자열 등의 데이터가 더 이상 참조reference되지 않을 때 자동으로 메모리를 해제하여 반환하도록 관리하는 가장 일반적인 메모리 관리 방식이다.

Mark & Sweep GC

마크 앤 스윕 가비지 컬렉션은 Mark 와 sweep 두 단계로 이루어진 알고리즘이다. 각 객체를 구성하는 메모리에는 가비지 컬렉션에서 사용하기 위한 1 bit 의 flag 값이 존재한다. 이 값은 가비지 컬렉션이 일어나는 동안에만 활성화 된다. 이 flag 가 참조 되고 있는지 마킹하고, 마킹되지 않은 flag 를 가진 객체들을 지우는게 마크 앤 스윕 가비지 컬렉션의 핵심이다.

Mark 단계는 마킹을 하는 단계라고 생각하면 이해가 쉽다. 먼저 Mark 단계가 시행되면, 접근 가능한 모든 object 들의 목록을 생성한다. 이를 GC root 라고 한다. root 란 코드에서 참조되는 전역 변수 등을 의미한다. 모든 root 들 중 접근 가능한 object 들의 flag 는 active 혹은 alive 로 분류되고 이들의 자식들 또한 재귀적으로 동일하게 처리된다. 이후 Sweep 단계에서 마킹되지 않은 객체들을 모두 garbage 로 간주하여 해당 메모리를 반환한다.

Reference counting

레퍼런스 카운팅은 객체를 참조하는 변수의 수를 추적하는 하나의 가비지 컬렉션 방식이다. 참조reference 가 하나 증가할 때마다 레퍼런스 카운트가 1씩 증가한다. 즉, 레퍼런스 카운트가 0이 된 객체는 더 이상 참조되고 있지 않다는 뜻이므로 가비지로 간주하여 메모리를 반환한다.

What is V8 Engine

Node.js 공식 웹사이트에 가면, 'Node.js®는 Chrome V8 JavaScript 엔진으로 빌드된 JavaScript 런타임입니다' 라고 소개 되어있다. 그렇다면 V8 Engine 은 대체 무엇일까?

V8 Engine 은 구글이 제공하는 강력한 오픈소스 자바스크립트 엔진이다. 자바스크립트는 인터프리터 언어이기 때문에 코드를 마이크로프로세서가 이해할 수 있는 더 낮은 수준의 언어 혹은 기계어로 변환하고 실행하는 엔진이 필요하다. 다른 엔진으로는 Rhino, JavaScriptCore, SpiderMonkey 등이 있다.

V8 Engine 의 메모리 시각화

자바스크립트는 싱글 스레드이기 때문에 V8 Engine 은 컨텍스트(context, 프로그램이 메모리에 올라갔을 때 실행되기 위해서 필수로 필요한 메모리 영역이나 레지스터 값 등의 리소스를 총칭하는 말) 당 한 개의 프로세스를 사용한다. 또, 각각의 프로세스 마다 하나의 스택을 가진다. 실행중인 프로그램은 V8 프로세스에서 할당된 일정 량의 메모리로 표현되는데, 이를 Resident set 이라고 한다.

Resident set 은 위와 같이 구성되어 있으며, 스택에서 힙으로 Memory 가 이동하는 방식은 여기서 확인 가능하다.

힙 메모리는 메모리 영역에서 가장 큰 블록이면서 가비지 컬렉션이 발생하는 곳이다. 힙 메모리 전체에서 가비지 컬렉션이 실행되는 것은 아니고, New space 와 Old space 부분에서만 실행된다. 힙메모리는 더 세부적으로 나눌 수 있으며 Large object space 를 제외한 각각의 공간들은 OS 가 mmap(메모리 맵핑. 파일을 특정 공간에 매핑해 두는 것) 할당한 여러개의 페이지로 이루어져 있다.

New space (Young generation)

새롭게 생성된 객체들이 존재하는 공간이다. 이 객체들은 짧은 생명 주기를 가진다.

New space 는 두 개의 semi space 를 가지는데, 이 semi space 들은 Scavenger 라는 minor gc 가 관리한다.

Old space (Old generation)

minor gc 가 두 번 발생하는 동안 New space 에서 살아남은 객체들이 이동하는 영역으로, Mark & Sweep 등의 major gc 를 관리하는 영역이다. Old space 는 각각 Old pointer space 와 Old data space 로 나뉜다. Old pointer space 는 살아남은 객체들을 가지며, 이 객체들은 다른 객체를 참조한다. Old data space 는 다른 객체를 참조하지 않는, 데이터만 가진 객체들을 가진다.

Large object space

다른 영역의 제한된 크기보다 큰 객체들이 있는 영역으로, 자체 mmap 메모리 영역 파일을 갖는다. 해당 공간의 object 들은 gc 로 이동되지 않는다.

Code space

실시간 JIT(just in time) 컴파일러가 컴파일 된 코드 블록들을 저장하는 공간이다.

Cell / Property / Map sapce

각각 cells / properties / maps 를 저장하는 공간이며, 이 공간에 저장되는 객체들은 전부 같은 사이즈이다.

V8 Memory management

V8 Engine 은 가비지 컬렉션을 사용해 힙 메모리를 관리한다. V8 gc 는 세대적이다. 힙 메모리 내의 객체들은 수명에 따라 그룹화되고 다른 단계에서 제거된다.

minor gc Scavenger

minor gc 는 New space 를 작고 깨끗하게 유지시킨다. 이 공간에 할당되는 객체들은 비교적 사이즈가 작으며 단순한 포인터를 통해 할당이 이루어지기 때문에 할당 절차의 비용 또한 작다. New space 가 다 차서 할당 포인터가 더 이상 새로운 객체를 포인트 할 공간이 없어지게 되면 minor gc 가 발동된다. 이 과정을 scavenger gc 라고 한다.

major gc

major gc 는 Old space 를 관리하며, V8 Engine 이 Old space 에 여유 저장 공간이 없다고 판단될때에 발동한다. scavenger 는 작은 데이터들을 관리하는데 유용하지만 비교적 큰 사이즈의 힙 메모리 관리를 잘 수행해내지 못한다. 큰 사이즈의 힙 메모리 관리는 Mark & Sweep 을 통해 gc 처리를 진행한다.

Stop-the-world process

가비지 컬렉션이 수행되는 동안 애플리케이션이 멈추는 것을 stop-the-world 라고 한다. minor gc 의 경우 과정이 굉장히 빠르고 효율적으로 이루어지므로 stop-the-world process 를 무시할 수 있으나, major gc 는 그렇지 않다. 따라서 V8 에서는 major gc 가 수행되는 동안 Incremental GC, 동시 마킹 등의 기술 들을 사용하여 부작용을 최소화 하고자 한다.

'javascript' 카테고리의 다른 글

scope & variable  (0) 2021.05.02
data type  (0) 2021.05.01
javascript  (0) 2021.04.27