Post

자바스크립트 동작 원리, 메모리 구조, 이벤트 루프

자바스크립트 엔진

  • 자바스크립트 코드를 실행하는 프로그램 혹은 인터프리터

  • 각각의 엔진이 서로 다른 웹 브라우저, Node.js와 같은 런타임 등에서 동작할 수 있다.

  • javascript 대표 엔진으로는 Google V8 엔진이 있다.

메모리 구조

  • Memory Heap

    • 메모리 할당이 일어나는 곳으로서 구조화되지 않은 넓은 메모리 영역을 지칭
    • 변수, 함수의 저장/호출 작업이 발생하는 메모리 공간
  • Call Stack

    • 코드를 읽어내려가면서 실행할 함수를 밑에서부터 호출 스택을 하나씩 쌓고, 메모리 힙에서 작업 수행에 필요한 것들을 찾아서 작업을 수행하는 공간
    • 호출 스택의 각 항목을 스택 프레임이라고 한다.


메모리 구조 예시

1
2
3
4
5
6
7
8
9
function Animal() {}
// stores `new Animal()` instance on memory address 0x001232
// tiger has 0x001232 as value in stack
const tiger = new Animal();
// stores `new Object()` instance on memory address 0x000001
// `lion` has 0x000001 as value on stack
let lion = {
  strength: "Very Strong",
};

tiger와 lion은 참조형 타입이다.

코드가 실행되다 Animal()의 인스턴스를 생성하면 Heap의 메모리 주소 어딘가에 저장된다.

변수 tiger는 스택에 쌓이게 되고 그 value로 Animal()의 Heap 메모리 주소를 갖게 된다.

즉, tiger 변수에 접근하면 value가 가리키고 있는 Heap 메모리 주소로 이동하고 거기에 저장된 value를 꺼내오는 것이다.




동작 구조

  • Web APIs : 브라우저에서 제공하는 API

  • 이벤트 루프(Event Loop) : 콜 스택이 비어 있을 때 콜백 큐의 첫 번째 콜백을 넘겨준다.

동작 순서

동기 방식 이벤트 처리

[1] javascript 엔진이 코드를 순차적으로 실행한다.

[2] 코드 실행 중 함수를 발견하면 콜 스택에 넣는다.

[3] 콜 스택에 넣은 함수를 실행한다.

[4] 실행된 함수는 콜 스택에서 제거한다.

비동기 방식 이벤트 처리

[1] javascript 엔진이 코드를 순차적으로 실행한다.

[2] 코드 실행 중 비동기 이벤트 발견시 이벤트 함수를 콜 스택에 넣는다.

[3] 비동기 이벤트 함수의 Event handler를 콜백 큐에 넣는다.

[4] 콜 스택이 비어 있을 경우 콜백 큐에서 Event handler 하나를 꺼내서 콜 스택에 넣는다.

[5] 콜 스택에 넣은 함수를 실행한다.

[6] 실행된 함수는 콜 스택에서 제거한다.

콜 스택 동작 예제


[1] 자바스크립트 파일이 실행되면서 동시에 (anonymous)라는 함수가 콜 스택의 가장 아래에 들어온다. 이를 Global Execution Context라고 하는데 나중에 자세히 살펴보자. 함수 이름이 (anonymous)인 이유는 자바스크립트 파일의 이름이 현재 없어서 그렇다.

[2] 가장 아래에 작성된 코드인 calculate() 함수를 읽음과 동시에 이 함수가 콜 스택에서 두번째 밑으로 들어온다. 그리고 이를 수행하기 위해 calculate() 함수가 선언된 코드 안 쪽으로 들어가게 된다.

[3] sumTotal을 계산해서 그 값을 얻어낸 후 다음 줄로 내려간다.

[4] 이 코드를 실행하기 위해서는 subtractTwo() 함수를 알아야하기에 당장 calculate() 함수가 콜 스택에서 제거되지 못하고, 대신 subtractTwo() 함수가 콜 스택에 새롭게 쌓인다. 그리고 이 함수를 실행하기 위해 함수가 정의된 곳으로 간다.



[5] subtractTwo() 함수의 값을 계산하여 값 7을 얻어냈다.

[6] 계산된 값을 토대로 subtractTwo() 함수를 실행했으므로 이 함수는 콜 스택에서 제거된다.

[7] calculate() 함수 또한 실행에 필요한 모든 값을 얻었으므로, 실행되고 콜 스택에서 제거된다.

[8] 마지막으로 본 파일의 실행이 모두 끝났으므로 Global Execution Context인 (anonymous) 함수 또한 콜 스택에서 제거되고, 모든 수행이 종료된다.

콜백 큐 (비동기 이벤트) 동작 예제

1
2
3
4
5
6
alert("A");
setTimeout(function () {
  alert("B");
}, 0);
alert("C");
// A, C, B


[1] alert('A')를 콜 스택에 넣고 alert를 실행 후 콜 스택에서 제거

[2] setTimeout 을 콜 스택에 넣고 setTimeout의 Event handler를 콜백 큐에 넣은 후 setTimeout을 콜 스택에서 제거

[4] alert('C')를 콜 스택에 넣고 alert 실행 후 콜 스택에서 제거

[5] 콜 스택이 비어있을 경우 콜백 큐에서 Event handler 하나를 콜 스택에 넣은 후 콜백 큐에서 Event handler 하나를 제거

[6] 콜 스택에 존재하는 Event handler를 실행 후 콜 스택에서 제거



1
2
3
4
5
6
for (var i = 0; i < 3; i++) {
  setTimeout(function () {
    alert(i);
  }, 1000);
}
// 3, 3, 3

1, 2, 3이 아닌 3, 3, 3이 출력되는 이유

javascript 엔진이 먼저 코드를 실행 후 비동기로 동작하는 setTimeout Web API 를 3번 실행하였기 때문에 현재 콜백 큐에는 3개의 Event Handler가 들어가있다.

그래서 모든 코드 실행 후 마지막으로 콜백 큐에 등록된 Event Handler가 처리되기 때문에 현재 for 문의 i 값이 3이므로 실행되는 Event handler는 3을 3번 출력하는 것이다.

참고사이트

[javascript] 4. 자바스크립트 실행 순서
자바스크립트의 동작원리: 엔진, 런타임, 호출 스택
자바스크립트 런타임 : 콜 스택과 메모리 힙
JS 메모리 구조

This post is licensed under CC BY 4.0 by the author.