-
[Javascript] 자바스크립트 엔진(heap & stack)개발일지/FE 2023. 2. 23. 11:06반응형
우리가 웹상에서 웹사이트를 볼 수 있는 것은
브라우저에서 javascript를 실행할 수 있는 환경을 제공한다는 의미이다.
브라우저는 Browser APIs나 다른 요소들이 있는데 이번에 살펴볼 것은 javascript 엔진이다.
javascript는 인터프리터 언어이다.
인터프리터란 코드를 실행함과 동시에 한 줄씩 실행한다는 의미이다.
다른 언어를 예를 들어 보자면 우리는 컴파일이라는 과정이 존재하고
(High level 언어를 low level 언어로 바꾸어주는 과정
즉, 영어로 코딩한걸 머신이 이해하기 쉬운 기계어로 바꾸어준다는 의미)
그 후 실행 명령어를 통해 해당 코드를 실행했다.
그러나 javascript 엔진은 그 중간 과정을 생략하고 실행과 동시에 컴파일을 진행한다.
그래서 javascript를 인터프리터 혹은
just-in-time 컴파일 프로그래밍 언어라고 하기도 한다.
위에서 언급했듯이 javascript는 컴파일 과정이 있느냐고 물어본다면,
당연히 컴파일 과정이 존재한다.
그러나 컴파일이 되는 동시에 실행이 되고 있으므로
때로는 컴파일이 다 되기도 전에 인터프리터가 실행을 이미 완료했을 수 있다.
아무튼 본론으로 돌아가서 javascript에는(정확히는 javascirpt를 실행시키는 브라우저에는) javascript 엔진이 들어있다.
이 엔진은 크롬, 파이어폭스 등등 브라우저 별로 다를 수 있는데
대표적인 크롬은 v8엔진이 존재한다.
javascript 엔진에는 두 가지 요소가 존재한다.
바로 heap과 stack이다.
heap은 보통 메모리 할당과 관련이 있다.
또한 heap은 장기 메모리이고
반대로 stack의 경우 실행과 관련이 있으며
단기 메모리라는 점이 있다.
조금 더 자세하게 예를 들어보면, 다음과 같은 코드를 볼 수 있다.
function getName(){ return prompt('Your Name:',''); } function greet(){ const name= getName(); console.log('Hello'+name); } greet();
일전 hoisting과 관련된 이야기를 한 적이 있다.
[Javascript] Hoisting과 TDZ
Javascript는 var, let, const와 같은 변수 생성 방법이 존재한다. 우리는 간혹 Hoisting을 생각할 때 var과 함수는 Hoisting 되지만 let과 const는 호이스팅이 되지 않는다고 생각한다. 그러나, 이는 틀린 말이
coding7281.tistory.com
javascript는 실행이 됨과 동시에 변수와 함수를 코드의 최상단으로 올려 메모리를 할당한다.
(실제로 최상단으로 코드가 올라간다는 의미는 아니다. 개념적으로 이해하면 좋을 것 같다.)
이때 함수는 함수의 이름으로 초기화된다.
그러니까 함수는 먼저 장기 메모리인 heap에 등록이 된다는 의미이다.
함수는 보통 정의 작성 후 여러 번 불릴 수 있고
코드가 실행되는 도중 다른 함수의 반환 값을 이용할 수 있기 때문에
함수를 지웠다가 불리는 그때마다 다시 저장하기에는 비용과 메모리의 낭비가 존재할 수 있다.
그래서 함수로 선언된 것들은 heap 등록을 통하여 불릴 때마다 execution context(실행 컨텍스트)로 넘어간다.
그러면 이쯤에서 앞에 내용을 정리할 필요가 있다.
heap은 장기 메모리이자 메모리 할당과 관련이 있었다면
stack은 단기 메모리이자 실행과 관련이 있었다.
즉 stack은 프로그램의 흐름(실행 컨텍스트)을 관리한다고 할 수 있다.
주로 현재 실행하고 있는 함수를 관리하는 역할이라고 생각하면 되는데
예를 들어 새로운 함수를 실행할 때 현재 실행되고 있는 함수 중 어떤 함수가 데이터를 반환하는지 관리한다.
우리가 코드를 작성하고 실행시키면 스택은 익명함수(anonymouse)부터 실행한다.
anonymouse는 쉽게 말하면 script 파일 그 자체라고 생각해도 될 것 같은데
별도의 이름이 존재하는 것은 아니다.
이 익명 함수가 스크립트를 감싼 전체 포장지이고
이 함수가 실행되면서 스택에 차곡차곡 기록이 쌓이게 된다.
stack 확인 위의 사진은 chrome 엔진 디버거를 통한 호출 스택 확인이다.
이렇게 anonymouse이 스택에 쌓인 다음 greet 함수가 호출되면서 스택에 쌓이고,
greet 내부에서 getName 함수가 호출된다.
스택은 LIFO로 마지막에 들어온 것이 첫 번째로 나가는 특징이 있으며
한 함수가 실행되고 있을 때 해당 함수의 반환 값이 필요한 함수는 대기 상태가 되므로 단일 스레드임을 알 수 있다.
단일 스레드는 함수의 실행 순서를 보장한다는 의미이기도 하다.
이제 javascript 엔진의 두 부분 heap과 stack을 모두 확인했다.
우리에게는 javascript 엔진뿐만 아니라 event loop 같은 다른 요소도 존재한다.
예를 들어, 다음과 같은 코드가 있다고 생각해 보자.
const startBtn = document.getElementById("start-btn"); function plusCounter() { let count = parseInt(startBtn.innerText); startBtn.innerText = count + 1; } startBtn.addEventListener("click", plusCounter);
다음과 코드는 버튼을 누른 만큼 숫자를 카운트하는 js 코드이다.
이때, 위의 javascript 엔진이 실행하는 함수를 실행 컨텍스트에 옮겨 LIFO로 진행한다고 했으니
버튼이 누를 때마다 실행 컨텍스트에서 해당 함수가 돌아간다고 생각할 수 있다.
그러나 놀랍게도 코드를 실행하고 부착된 EventListener에 중단점을 두면 다음과 같은 결과를 볼 수 있다.
스크립트 실행과 동시에 anonymouse 함수가 등록되었다가
실행이 끝나면 빈 stack이 된다.
그러나 버튼을 누를 때마다 innerText에 의해 카운트는 올라간다.
어떻게 가능한 것일까?
💡 여기서 확인할 수 있는 것이 바로 이벤트 리스너는 비동기와 관련이 있다는 것이다.
OS 그러니까 운영체제는 실행되는 작업이 존재하고
여러 작업을 실행하기 위해서는 운영체제에서 작업별 일정 부분의 메모리를 할당한다.
그러니까 크롬 브라우저가 돌아가기 위해서 OS에서 크롬에게 메모리를 할당하면
이 할당된 메모리 내에서 브라우저가 할당된 메모리를 heap을 통하여 관리하게 되는 것이다.
즉 브라우저 엔진이 heap을 수행한다는 의미이다.
이때, addEventListener과 같이 이벤트를 부착시키는 곳마다 지속적으로 감시하게 될 경우
메모리의 낭비가 심각해지고 할당된 메모리를 전부 사용할 수 있다.
그렇게 되면 실행이 지연될 뿐 아니라 멈춰버릴 수 있는 것이다.
그래서 javascript 엔진은 진행 중인 리스너에 대한 관여를 하지 않는다.
대신 브라우저가 이 리스너를 관리한다.
금일은 javascript 엔진 - heap과 stack에 대해 알아보았다.
추가로 eventloop도 잠깐 알아보았으나 이는 아마 차후 다시 한번 정리할 기회가 있을 것 같다.
💜
해당 게시글은 MDN 공식 문서 및 Udemy 강의를 참고하여 만들어졌습니다.
틀린 부분이 있다면 댓글 주시면 수정하겠습니다.
감사합니다 😊
반응형'개발일지 > FE' 카테고리의 다른 글
[JavaScript] 자바스크립트란? (0) 2024.07.16 [Javascript] Hoisting과 TDZ (0) 2023.02.21 [Javascript] 원시 값과 참조 값 (0) 2023.02.21 [JavaScript] var vs let vs const (0) 2023.02.02 [JavaScript 완벽가이드] 89. 게임 리셋기능 (2) 2023.01.15