요약
- v8 엔진은 크롬 브라우저에서 돌아가는 JavaScript엔진으로 “소스코드 - 파싱 - AST - Ignition로 바이트 코드 생성 - 실행 - Turbo Pan 최적화” 순서로 동작된다.
내용
- v8은 구글 크롬 브라우저용으로 개발한 JavaScript, WebAssembly 엔진이다.
동작방식
1. 소스 코드 입력
- JavaScript 소스 코드를 입력 받는다.
- 런타임 환경에서 동적으로 로드된다.
2. 파싱
- 어휘분석 (Lexical Analysis)
- 자바 스크립트 코드를 토큰으로 분할한다.
- 변수, 키워드, 연산자, 리터럴 등으로 분할한다.
let x = 10 + 5;
을 토큰 생성하면 다음과 같다.
- let 키워드
- x 식별자
- = 연산자
- 10 리터럴
-
- 5 리터럴
- ; 세미콜론
- 스캐너로 입력된 소스 코드를 한 줄씩 읽어서 각 줄에서 토큰 생성을 한다. 이 과정에서는 공백이나 주석은 무시된다.
- 구문 분석 (Syntactic Analysis)
- 생성된 토큰을 기반으로 프로그램의 구조를 정의하는 추상 구문 트리(Abstract Syntax Tree, AST)를 생성한다.
let x = 10 + 5;
을 구문 분석 하면 다음과 같다.
- 의미 분석(Semantics Analysis)
- 생성된 AST를 기반으로 변수와 함수의 유효성 및 타입 정보를 확인한다.
3. 바이트코드 생성
- V8은 생성된 AST를 바이트 코드로 라인 단위로 변환한다.
- V8에서 생성된 바이트 코드는 2가지 주요 요소를 포함한다.
- 인터프리터 Ignition에서 실행한다.
- V8이 지원하는 명령어 집합으로 표현된다.
- 위 코드를 바이트 코드로 표현하면 다음과 같다. (파싱은 끝났다고 가정한다.)
4. 실행
- 생성된 바이트코드를 V8의 Ignition이 실행한다.
- 인터프리터 형식으로 라인 단위로 실행된다.
- 함수 호출시 V8은 새로운 스택 프레임을 생성한다.
- 스택 프레임은 함수의 실행 컨텍스트(변수, 매개변수, 호출 스택 등)을 유지한다.
- 각 명령어에 해당하는 작업은 다음과 같다.
- 변수 로드
- 연산 수행
- 조건문 및 반복문 처리
- 함수 호출
- 프로파일링
- Ignition는 코드가 실행되는 동안 성능 데이터를 수집한다.
- 자주 호출 되는 함수나 코드 경로에 대한 정보를 바탕으로 최적화가 필요한 코드를 식별한다.
5. 최적화
- JIT 컴파일 (Just-In_Time Compilation)
- 자주 실행되는 바이트코드(hotcode)를 Turbofan이라는 JIT 컴파일러를 사용하여 최적화된 기계어 코드로 변환한다.
- 다음과 같은 최적화 기법이 사용된다.
- 인라인 캐싱: 객체의 프로퍼티 접근을 최적화한다.
- 타입 특화: 타입 정보를 활용하여 해당 타입에 맞는 코드를 생성한다.
- 불필요한 코드 제거: 실행되지 않는 코드나 불필요한 연산을 제거한다.
- 가비지 컬렉터
- 사용되지 않는 메모리를 회수하여 메모리 누수를 방지하고 효율적인 메모리 관리를 보장한다.
- Mark-and-Sweep 알고리즘으로 객체의 참조 그래프를 탐색하여, 살아 있는 객체를 마크한다. 이후 마크되지 않는 객체는 메모리에서 해제 된다.
- Generation Garbage Collection, 객체의 생명 주기에 따라 세대별로 관리하여 단기 객체와 장기 객체를 구분한다. 짧은 생명 주기를 가진 객체는 자주 수집하고, 긴 생명 주기를 가진 객체는 덜 수집한다.
- v8에서 프로그램이 실행되면 메모리의 Resident Set이라는 빈 공간이 할당된다. 스택 영역과 힙 영역으로 나눌 수 있다. (자바스크립트는 싱글 쓰레드라고 하나의 스택 메모리를 가진다.)
- 힙 - New space는 새로 만들어진 Object가 저장된다.
- 힙 - Old space는 마이너 가비지 컬렉션이 2번 발생할 동안 살아남은 객체 들이 저장된다.
- Pointer space: 다른 객체를 참고하는 객체(다른 객체의 포인터를 가진 객체)
- Data space: 문자열, 실수 등 데이터만을 가진 객체
참고