2탄_브라우저의 자바스크립트 엔진은 js를 어떻게 해석하고 실행하는가 ?

수박·2021년 8월 13일
8

browser

목록 보기
3/4
post-thumbnail

이전 글에서 다루지 않았던 자바스크립트 엔진에 대한 글입니다.


HTML이 parsing될 때 script태그를 만나면 parsing이 일시중지되고 script를 해석, 실행한다.

이 해석, 실행되는 것이 어떻게 이루어지는가에 대한 정리를 시작해보자.


앞서,

컴파일러, 인터프리터, JIT 컴파일러에 대해 간단히 알아보자.

알아야 아래 내용 이해하기 편하다.


컴파일러 ?

프로그래밍언어로 작성된 것을 실행파일로 만들기 위한 목적파일로 만드는 언어 번역 프로그램을 컴파일러라고 한다.

CPU는 0,1만을 읽는 단순한 계산기다.

영어로 이루어진 코드를 컴퓨터는 절대 알 수 없다. 0, 1로 번역시켜주어야한다.


Source code가 실행파일로 만들어지는 과정_출처

C로 예를 들었을 때 영어로 작성된 a.c 라는 source code를 목적 파일로 바꾸는 과정을 컴파일이라고 한다. 목적파일은 실행될 수 있는 파일이 아니다.

gcc -c a.c 를 통하면 a.o 파일이 생긴다.이 때 a.o가 목적파일이다.

목적파일은 컴퓨터가 실행할 수 있는 완벽한 기계어가 아니다.

이 목적파일은 바로 실행될 수 없다. 그럼 ?

링커를 통해야 기계가 실행할 수 있는 실행파일a.out으로 실행할 수 있다.

왜 바로 실행파일로 안바꾸냐 ? 왜 링크과정을 거치느냐 ?에 대한 답은 다음과 같다.

한 파일에서 모든 코드를 작성해 실행하면 링크과정은 사실 불필요하다.

다만, 코드가 100개 1000개의 파일로 나뉘어져있다고 했을 때 이 파일들을 연결시키는 과정이 필요하다.

목적파일들을 하나로 통합시켜 하나의 실행파일로 만들기 위해 링크과정이 필요한 것이다.


인터프리터 ?

프로그래밍언어를 한 줄씩 읽어서 실행하는 프로그램이다.

인터프리터는 바로 런타임환경에서 기계어로 번역해서 실행하는 역할을 한다.

그게 다다.


JIT 컴파일러 ?

Just in Time으로 실시간(런타임) 컴파일기법이라고 한다.

실행하는 시점에 얘가 실행도하고 번역도 한다는게 아니다.

바이트 코드를 기계어로 번역하는 역할을 한다. (바이트코드란, VM이 읽어 실행할 수 있는 기계어다. CPU가 읽을 수 있는 기계어랑은 다르다.)

바이트코드에서 번역된 기계어는 캐시에 저장되어있기 때문에 재사용시 다시 번역할 필요가 없다.

또한 반복되는 코드가 들어올 때 인터프리터가 다시 기계어로 번역하는 과정이 필요없이 번역된 것을 사용하면 되므로 해당 시간이 단축된다.

JIT가 컴파일도하고 인터프리터랑 같이 쓰인다는 건 OK - 누가 쓰는데 ?

Java가 이를 사용하고 있다_출처


🤔 JIT? 이걸 왜 사용하는데?

같은 연산을 100번 실행하는 코드가 있을 때 인터프리터 방식은 같은 줄을 100번 재번역해야한다.

컴파일방식의 경우 미리 기계어로 모든 결과를 번역해놓기에 문제가 되진 않지만,

인터프리터는 한줄한줄 읽어 실행시킨다는 점에 있어서 비효율적일 수 밖에 없다.

따라서 이런 비효율성을 없애기 위해 브라우저는 캐시를 사용할 수 있고 JIT컴파일 방식을 혼합해 반복되는 코드를 미리 기계어로 번역해두어 최적화가 가능함으로 사용하기 시작했고, Chrome의 V8에서 사용되고 있다.(이외에도 많이)

js엔진을 인터프리터로 구현하거나 JIT를 사용해서 구현하기에

브라우저마다 js를 해석, 실행하는 방식은 차이가 있다.

이렇듯 js는 컴파일도되고 인터프리터로 실행이되는 언어라고 할 수 있겠는데

그럼 브라우저의 js엔진은 뭘 하길래 해석하고 실행할 수 있는가 ?


자바스크립트 엔진 ?

공식문서와 포스팅들을 참고하며 정리한 내용입니다.

자바스크립트 엔진은 load된 script를 해석, 실행하는 역할이다. 가장 많이 사용되고 있는 엔진은 크롬과 node의 V8이다.

앞서 자바스크립트 엔진 내부에서는 컴파일과정이 있다고 했다. 그럼 JS는 컴파일언어일까 ? 인터프리터 언어일까 ?

결론부터 얘기하면 컴파일도 하고 인터프리터도 하지만, 대외적으로 인터프리터언어라고 다들 부른다.

어느 부분에서 컴파일이 일어나는지 궁금해져서 찾아보았다. 엔진이 시작되는 지점부터 코드를 실행하기까지의 과정을 보자


자바스크립트 엔진이 코드를 넘겨받는 시점

랜더링 엔진은 HTML을 읽다가 <script>를 만나면 잠시 작업을 일시중단 한다.

이 때 읽어들인 Js코드가 자바스크립트 엔진에서 해석, 실행될 때까지 멈추는 것이다.

코드를 넘겨받고 해석하는 과정은 다음과 같다.

해석 == 컴파일레이션

해당 과정을 컴파일레이션이라고 한다.

출처-공식문서

엔진은 text형식인 코드를 실행하기 전 컴파일 과정을 거친다.

컴파일은 총 3개의 단계로 이루어져 있다.

  1. 코드를 의미 있는 조각으로 나누는 렉싱/토크나이징 (이때 스코프가 결정된다 - 렉시컬스코프라 부르는 이유)
  2. 코드를 트리구조로 나타내는 AST로 만드는 파싱,
  3. VM이 실행할 수 있도록 트리를 가지고 바이트코드로 변환하는 컴파일 과정으로 이루어져있다.

컴파일레이션을 통해 JS코드가 실행되기 직전에 컴파일된다는 것을 알 수 있었다.

해당 컴파일레이션 과정을 통해 스코프와 호이스팅까지 이해할 수 있으므로 관련글을 꼭 읽도록 하자.

AST를 바이트코드로 변환하는 것은 Ignition라는 이름의 Interpreter이다.

Interpreter가 VM이 실행할 수 있는 기계어로 번역하는 것을 여기선 컴파일이라고 표현했다. - 컴파일러만 컴파일하는게 아니다.


실행

컴파일되었으면 실행해야지 !

Ignition이라는 Interpreter가 바이트코드를 만들고, 실행함으로써 코드가 동작된다.

? 컴파일러는 또 뭐죠? 인터프리터는 또 뭐죠 ? JIT 컴파일한다고 했잖아요 그럼 그것만 하면 되잖아요 그게 짱이잖아요!

JIT를 사용하는 이유가 반복되는 빈도가 높아질 때 만들어 놓은 기계어를 재사용할 수 있어서라고 했는데 만약 자주 반복되는 코드가 거의 없다면 기계어(native code라고 표현)를 수행하는 시간보다 만드는 시간이 더 걸릴 수 있다.

따라서 JIT가 무조건적으로 좋은 방법은 아니다.

최근 엔진들은 adaptive compilation 방식을 택하는데 반복이 많이되면 유동적으로  JIT, 아니면 interpreter로 mode를 나눠 수행한다고 보면 이해하기 좋다. 더욱 자세한 내용은 이 글을 읽어보자

Ignition이 Interpreter고 TurboFan이 JIT Compiler!

이 두가지가 나뉘는 것을 찾으려고 얼마나 힘들었는가..


😝 마치며

급 마무리?

Js는 컴파일레이션을 통해 번역된 바이트코드로 VM상에서 실행된다.

사전(pre) 컴파일 이후 인터프리터로 실행되며 반복되는 코드는 JIT 컴파일러를 통해 기계어로 만들어진다.

자주 사용되는 코드들은 번역없이 캐싱된 기계어를 통해 실행되어 최적화를 이룬다.

이 글에서 중요한 포인트는 컴파일레이션시 스코프, 호이스팅 관련 부분-

다음 글은 자바스크립트 엔진의 구성요소와 이벤트루프의 동작방법에 대해서 작성할 예정이다.

끝!



참고

3개의 댓글

comment-user-thumbnail
2021년 8월 13일

웁스.. 저 자바스크립트 엔진에 대해서 되게 간략하게만 쓰고 말았었는데.. 2탄 보고 반성합니다.... 진짜 한번 공부할 때 제대로 하는 수박님..! 한수 배우고 갑니다👍

1개의 답글
comment-user-thumbnail
2021년 8월 14일

헐..이 부분 조금 어렵네요...JIT부터 막혀버렸어요....ㅠㅜ 이 부분 잘 알면 js 이해하는데 좋을 것 같은데 두고두고 보러 와서 이해할게요! 이렇게 정리해 주셔서 감사합니다😫

답글 달기