ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Call Stack
    33-js-concepts 2019. 10. 28. 13:02

    개인적으로 이해한 수준에서만 글을 남깁니다.
    추후 더 많은 지식을 얻게 된다면 보충할 생각입니다.
    이번 글에서는 비동기를 위한 이벤트 루프 및 web api, callback queue에 대한 설명은 제외하겠습니다.
    사견은 되도록이면 (*)를 이용하여 작성하였습니다.

    호출스택(Call Stack)이란?

    Javascript의 경우는 단일 스레드 엔진을 사용하므로 힙(메모리 할당 영역)과 호출 스택이 존재합니다. 호출스택은 기본적으로 LIFO(Last In First Out)로 후입선출과정을 거칩니다. 마지막에 들어간 함수가 먼저 실행되고 리턴됩니다. (*호출스택은 함수를 위해 존재한다 생각합니다. 결국 함수의 처리 순서를 결정하게 되는 것이니까요.)

    // 호출스택의 기본 동작을 보겠습니다.
    function func1() {
      throw new Error('error');
    }
    function func2() {
      func1();
    }
    function func3() {
      func2();
    }
    func3();

    위 코드를 수행했을 때 우리는

    VM293:2 Uncaught Error: error
        at func1 (<anonymous>:2:9)
        at func2 (<anonymous>:5:3)
        at func3 (<anonymous>:8:3)
        at <anonymous>:10:1

    다음과 같은 형태에 에러를 볼 수 있는데 여기서 호출스택을 확인할 수 있습니다.
    호출스택에 스택프레임(Stack Frame) 단위로 적재가 되고 에러가 발생하면 스택트레이스(Stack trace)를 보여줍니다.

        Call Stack 
    |    func1                |
    |    func2                |
    |    func3                |
    |    anonymous(\*global)  |

    로 적재되어 있고 정상적으로 동작하였다면 순차적으로 func1 -> func2 -> func3 순서로 리턴하며 호출 스택이 종료됩니다.
    하지만 현재 func1에서 에러가 발생하였고, 호출스택에 적재된 순서(호출 순서)는 다음과 같다는 것을 보여줍니다.

    호출스택이 정상동작한다면?

    function func1() {
      console.log('func1 success');
    }
    function func2() {
      func1();
      console.log('func2 success');
    }
    function func3() {
      func2();
      console.log('func3 success');
    }
    func3();
    //결과
    func1 success
    func2 success
    func3 success

    결과와 같이 콘솔에 찍히면서 마지막 func3에서 console.log()를 호출하며 호출스택이 마무리 됩니다.

    Maximum call stack size exceeded ?

    Uncaught RangeError: Maximum call stack size exceeded
        at func1 (<anonymous>:1:15)
        at func2 (<anonymous>:5:5)
        at func1 (<anonymous>:2:5)
        at func2 (<anonymous>:5:5)
        at func1 (<anonymous>:2:5)
        at func2 (<anonymous>:5:5)
        at func1 (<anonymous>:2:5)
        at func2 (<anonymous>:5:5)
        at func1 (<anonymous>:2:5)
        at func2 (<anonymous>:5:5)
        stack size 마지막영역
        |    func1                |
        |    func2                |
        |    ...                  |
        |    func1                |
        |    func2                |
        |    anonymous(\*global)  |

    과 같은 형태로 적재되다가 stack size의 마지막영역을 넘어서는 순간 발생합니다.
    그렇다면 위와 같이 발생하는 에러는 무엇일까요.
    보통 호출스택은 적재 가능한 최대 사이즈가 있습니다.
    해당 범위를 넘어섰을 경우 위와 같은 에러가 발생합니다.

    마치며.

    잠깐만 읽어도 제가 금방 이해할 수 있게 쓰려고 노력을 했습니다.(* 제 기억력이 순간순간만 유효한 느낌이 들어서요 :-( )
    제 기본 지식이 깊지 않음에도 이런 글을 쓰는 이유는, 나중에 이 글을 다시 보았을 때 부끄러움을 느낀다면 제가 더 발전한 것을 깨닫는 기회일 것이고, 다른 분들이 문제 사항을 발견해주신다면 제가 잘못 알고 있었던 것이니 그 얼마나 다행일까 싶어서입니다.
    물론. 이 글에 문제가 있었는데, 제가 파악하지 못하였고, 이 글을 보신 다른 분에게 문제가 간다면 그 역시 제가 죄송한 일일 것 같습니다.
    모쪼록 부족한 글 읽어주셔서 감사합니다.(_ _)

    언제나 모든 글 그리고 내용은 크로스 체킹이 기반되어야 하니 하기에 제가 참고한 글을 꼭 보아주시기 바랍니다.

    참고한 글

    How JavaScript Works: An Overview of the Engine, the Runtime, and the Call Stack — Alexander Zlatkov

    위의 글이 번역된 github ( 정말 감사한 github 입니다. )

    '33-js-concepts' 카테고리의 다른 글

    Primitive Types  (0) 2019.10.30

    댓글

Designed by Tistory.