하루에 한 문제

자바스크립트 런타임 : 콜스택과 메모리 힙 본문

Dev/JavaScript

자바스크립트 런타임 : 콜스택과 메모리 힙

dkwjdi 2021. 4. 20. 02:17
  • 인라인 캐싱
// Inline Caching
function findUser(user) {
  return `found ${user.firstName} ${user.lastName}`;
}

const userData = {
  firstName: 'Johnson',
  lastName: 'Junior'
}

findUser(userData); // 'found Johnson Junior'로 대체
  • 인라인 캐싱은 컴파일러에 의해서 자동적으로 수행되는 기능 중 하나이다.
  • 만약 위 코드에서 findUser(userData); 함수가 1백만번 반복적으로 실행된다면, 컴파일러는 userData 객체를 1백만번 찾는 대신, findUser(userData);  'found Jonhson Junior' 라는 텍스트로 대체하여 실행 속도를 대폭 향상시킨다.

 

 

히든 클래스

// hidden classes
function animals(x, y) {
  this.x = x;
  this.y = y;
}

const obj1 = new animals(1, 2);
const obj2 = new animals(3, 4);

obj1.a = 30;
obj1.b = 100;

obj2.b = 30;
obj2.a = 100;
  • 위의 경우, animals라는 객체의 값을 설정한 순서에 맞춰서 추후에 선언할 객체 또한 순서를 동일하게 정렬해야 한다.
  • Hidden Class는 컴파일러가 내부적으로 활용하는 도구 중 하나로, obj1과 obj2를 선언할 때 컴파일러은 두 객체가 동일한 hidden class를 공유한다고 인지한다.
  • 그런데 그 이후에 obj1와 obj2 객체의 값을 다른 순서로 부여하면 컴파일러가 헷갈려서 두 객체가 동일한 class를 공유한다고 인지하지 않게 되면서 최적화에 지장을 준다.

delete 키워드를 사용할 때 주의해야 하는 이유도 이와 맞닿아 있다. 예를 들어서 delete obj1.x = 30; 코드로 객체의 key를 삭제할 경우, obj1과 obj2는 더 이상 동일한 hidden class를 공유하지 않기 때문에 최적화가 어려워진다.

 


자바스크립트 엔진은 정보를 어디서 어떻게 저장하고 현재 어디를 실행하고 있는지 어떻게 알까?? 여기서 정보를 저장하는 공간이 메모리 힙이고 실행 중인 코드를 트래킹하는 공간이 콜 스택이다.

 

메모리 힙

const number = 610;
const string = 'some text'
const human = {
  first : "Andrei",
  last  : "Neagoie"
}

위의 코드처럼 우리가 변수를 선언한다는 것은 자바스크립트 엔진에게 '이러한 값을 저장할건데 메모리에 저장해주세요' 라는 말과 같다. 메모리 힙은 자바스크립트 엔진이 우리에게 제공하는 메모리의 넓은 영역이다. 우리는 이 메모리 힙에 임의의 데이터를 정렬되지 않은 상태로 보관할 수 있다

 

메모리 힙이란 변수나 함수의 저장과 호출이 발생하는 메모리의 일정 영역이고 이는 자바스크립트 엔진이 제공한다.

 

콜 스택

콜 스택도 마찬가지로 메모리에 존재하는 공간 중 하나이다. 코드를 읽어내려가며 작업들을 스택 형태로 저장하고 이를 통해 어느 부분이 실행중인지 트래킹 하기 위해 설계된 공간이다. 예시를 들어서 알아보자.

먼저 다음과 같은 코드를 크롬 개발자 모드에서 실행시켜보자.

function subtractTwo(num){
  return num - 2;
}

function calculate(){
  const sumTotal = 4+5;
  return subtractTwo(sumTotal);
  
}
debugger;
calculate()

1. F12 개발자 모드에 들어가서 Source -> New snippet에 위 코드를 작성하면 다음과 같이 Call Stack에 익명 함수가 들어가 있는 것을 확인할 수 있다. 이 익명함수는 현재 코드의 전체에 해당하는 자바스크립트 파일을 실행했기 때문에 콜 스택에 들어가 있는 것이다.(이 익명함수는 Global Execution Context 라고 한다.)

 

 

 

2. 현재 콜 스택에 calculate 함수가 있는 것이 확인가능하다.

 

 

3. return subtractTwo 구문을 만나면 콜 스택에 subtractTwo함수를 넣고 해당 함수로 이동하는 것을 확인할 수 있다. scope 에서 해당 스코프의 변수들도 확인할 수 있다.

 

4. return num-2 구문을 만나 Return value에 7을 저장하였다.

 

5. subtractTwo 함수가 콜 스택에서 제거되었고 지역 스코프에 맞게 num이라는 이름이었던 변수도 sumTotal로 변경되었다.

 

6. calculate 함수도 콜 스택에서 제거되고 지역 스코프를 갖는 변수들도 전부 사라졌다. 한 번더 실행하면 익명함수 또한 콜 스택에서 제거되고, 모든 수행이 종료된다.

최종적으로 콜 스택과 메모리 힙이 어떻게 동작하는지 나타내면 다음과 같다. 참고로 콜 스택에도 변수가 저장될 수 있고 , 객체나 배열 , 함수 등과 같은 복잡한 데이터들은 메모리 힙에 저장된다.

 

콜 스택을 사용하면 stack frame 으로 부터 현재 코드의 어느 부분을 실행중인지 알 수 있고, 메모리 힙을 참조하여 코드에 필요한 변수나 함수의 위치를 참조할 수 있다.

 

 

가비지 컬렉터

콜스택과 메모리 힙을 배우면서 각각의 공간은 무제한이 아니고 한정적임을 알 수 있다. 공간이 무한정 하다면야 크게 효율성에 대해서 고려하지 않을 수도 있지만, 콜스택과 메모리 힙은 언제나 한정적이기 때문에 이를 효율적으로 관리할 필요가 있다. 자바스크립트는 이 공간을 효율적으로 관리하기 위해서, 더 이상 효용가치가 없다고 판단되는 변수, 함수 등을 함수 실행 종료 후 메모리 힙에서 제거하는 동작을 수행한다. 필요한 데이터만 메모리 힙에 저장함으로써 메모리를 더욱 여유롭게 관리한다. 따라서 자바스크립트는 Garbage Collected Language라고 말할 수 있으며, 이러한 역할을 수행해주는 도구를 Garbage Collector라고 한다.

 

하지만 이는 자칫 자바스크립트 개발자들에게 잘못된 인상을 심어줄 수도 있다. '스스로 메모리 관리를 해주니까 내가 따로 메모리를 신경쓸 필요는 없지않을까?' 라고 생각할 수 있지만, 언제나 그렇듯 이 또한 프로그램에 지나지 않기 때문에 완벽하지는 않다.

그럼 이 Garbage Collector는 어떠한 원리로 작동하는 것일까?

 

# 마크 앤 스윕 알고리즘(Mark and Sweep Algorithms)

 

 

5. 메모리 누수

계속 메모리에 대한 이야기를 하고 있는데, 메모리 힙이 제대로 관리되지 않을 경우 메모리 공간의 범위를 넘어서서 정보들이 저장되는 경우가 생기는데, 이를 메모리 누수(Memory Leak)이라고 한다. 이는 과거에 사용되었고, 현재는 필요가 없음에도 불구하고 메모리 공간에서 제거되지 않고 공간을 차지하고 있는 현상을 의미한다.

 

1.global scope에서 전역 변수를 많이 만들 경우, 메모리 누수가 발생한다.

// 1. Global Variables
var a = 1;

2. 이벤트 리스너의 경우 사용이 완료되면 제거되도록 해야 하는데, 제거시키지 않을 경우 계속 이벤트 리스너가 추가되기 때문에 메모리 누수가 발생한다.

// 2. Event Listeners
var element = document.getElementById('button');
element.addEventListner('click', onClick);

3. setInterval() 함수의 경우 일정 주기마다 특정 작업을 수행하도록 지시해주기 때문에, 이는 계속 사용 중인 것으로 간주되어 가비지 컬렉터에 의해서 제거되지 못하고 메모리 공간을 계속 차지하게 된다.

// 3. setInterval
setInterval(() => {
  // referencing objects...
})

 

 

참고

engineering.huiseoul.com/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8%EB%8A%94-%EC%96%B4%EB%96%BB%EA%B2%8C-%EC%9E%91%EB%8F%99%ED%95%98%EB%8A%94%EA%B0%80-v8-%EC%97%94%EC%A7%84%EC%9D%98-%EB%82%B4%EB%B6%80-%EC%B5%9C%EC%A0%81%ED%99%94%EB%90%9C-%EC%BD%94%EB%93%9C%EB%A5%BC-%EC%9E%91%EC%84%B1%EC%9D%84-%EC%9C%84%ED%95%9C-%EB%8B%A4%EC%84%AF-%EA%B0%80%EC%A7%80-%ED%8C%81-6c6f9832c1d9

soldonii.tistory.com/53?category=862198

velog.io/@cheal3/JavaScript-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8%EC%9D%98-%EC%9E%91%EB%8F%99-%EC%9B%90%EB%A6%AC

'Dev > JavaScript' 카테고리의 다른 글

웹팩 로더  (0) 2021.04.27
프론트엔드 개발에 Node.js가 필요한 이유  (1) 2021.04.26
V8 작동원리  (0) 2021.04.20
Arrow Function에서 this바인딩  (0) 2021.04.20
Promise  (0) 2021.04.19
Comments