요약

  • 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;구문 분석 하면 다음과 같다.
{
  "type": "Program",
  "body": [
    {
      "type": "VariableDeclaration",
      "declarations": [
        {
          "type": "VariableDeclarator",
          "id": {
            "type": "Identifier",
            "name": "x"
          },
          "init": {
            "type": "BinaryExpression",
            "operator": "+",
            "left": {
              "type": "Literal",
              "value": 10,
              "raw": "10"
            },
            "right": {
              "type": "Literal",
              "value": 5,
              "raw": "5"
            }
          }
        }
      ],
      "kind": "let"
    }
  ],
  "sourceType": "script"
}
  • 의미 분석(Semantics Analysis)
    • 생성된 AST를 기반으로 변수와 함수의 유효성 및 타입 정보를 확인한다.

3. 바이트코드 생성

  • V8은 생성된 AST를 바이트 코드로 라인 단위로 변환한다.
  • V8에서 생성된 바이트 코드는 2가지 주요 요소를 포함한다.
    • 인터프리터 Ignition에서 실행한다.
    • V8이 지원하는 명령어 집합으로 표현된다.
let x = 10;
let y = 20;
let sum = x + y;
  • 위 코드를 바이트 코드로 표현하면 다음과 같다. (파싱은 끝났다고 가정한다.)
LOAD_CONST 10      // x에 10을 로드
STORE_VAR x        // x에 값을 저장
LOAD_CONST 20      // y에 20을 로드
STORE_VAR y        // y에 값을 저장
LOAD_VAR x         // x 값을 로드
LOAD_VAR y         // y 값을 로드
ADD                 // 두 값을 더함
STORE_VAR sum      // sum에 결과를 저장

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: 문자열, 실수 등 데이터만을 가진 객체

참고