메모리 힙 & 콜 스택

콜스택, 메모리힙?

자바스크립트 엔진이 구동되면서 코드를 읽고 실행하는 과정에서 중요한 두단계가 있다.

정보(ex 변수, 함수)를 특정한 장소에 저장하는 것과 현재 실행되고 있는 코드를 트래킹하는 작업이 그 두가지이다.

여기서 정보를 저장하는 공간(Memory Allocation이 발생하는 공간)을 메모리 힙(Memory Heap)이고, 실행 중인 코드를 트래킹하는 공간이 콜 스택(Call Stack)이다.

예시로 아래와 같은 코드는 콜스택 & 힙이 다음과 같이 구성된다.

const a = 10
const b = 35
const arr = []
function func() {
  const c = a + b; // 스코프 체인에 따라 GEC의 VO 참조
	obj = { d: c }
  return obj
}
const o = func()

image

콜스택

콜스택은 메모리에 존재하는 공간 중의 하나로, 코드를 읽어내려가면서 수행할 작업들을 밑에서부터 하나씩 쌓고, 메모리 힙에서 작업 수행에 필요한 것들을 찾아서 작업을 수행하는 공간이다.

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

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

위와 같은 코드가 있을 때 콜스택의 관점에서 살펴보면,

anonymous(GO) 라는 함수가 스택의 가장 밑에 쌓인다. 그 뒤 calcaluate 함수가 쌓이고 calculate함수가 스택에서 제거되기 전에 subtractTwo함수가 스택에 쌓이고 계산된 값을 리턴하면 subtractTwo, calculate, anonymous 순으로 스택에서 제거된다.

위와 같은 순서로 실행되면서 자바스크립트 엔진은 메모리 힙을 참고하여 코드 실행에 필요한 변수, 함수등의 위치를 참조하면서 실행한다. 참고로 JS의 원시형 ( String, Number, Symbol, undefined, null, boolean)과 실행 컨텍스트(Execution Context) 은 콜 스택에 저장되고, Object형 ( Object, Array, function )은 메모리 힙에 저장된다.


가비치 컬렉터

콜스택과 메모리힙의 공간은 한정적이다. 따라서 효율적으로 관리할 필요가 있다. 자바스크립트 엔진은 이를 위해 더 이상 효용가치가 없다고 판단되는 변수, 함수등을 함수 실행 종료 후 메모리 힙에서 제거하는 동작을 수행(참조의 여부로 컬렉팅을 수행) 한다. 필요한 데이터만 메모리 힙에 저장함으로써 메모리를 더욱 여유롭게 관리한다. 따라서 자바스크립트는 Garbage Collected Language라고 말할 수 있고 이러한 역할을 수행하는 도구를 Garbage Collector라고 한다.

가비치 컬렉터가 작동하는 원리는 Mark and Sweep Algorithms을 사용한다.

Mark And Sweep Algorithm

가비지 컬렉터에는 GC Root가 있다. GC Root는 말그대로 가비지 컬렉터의 Root를 의미하고 힙 외부에서 접근하는 변수나 오브젝트가 될수있다.

  1. GC Root에서 시작해서 참조가 가능한 값들을 가비지 컬렉터는 Mark한다.
  2. Mark가 끝나면 힙 내부를 돌면서 Mark되지 않은 메모리들을 해체한다( OS에 반환한다. ). 이를 Sweep이라고 한다.

메모리 누수( Memory Leak )

자바스크립트에서 발생할 수 있는 메모리 누수에는 몇가지 일반적인 케이스가 있다.

1. 우발적으로 생성된 전역변수 그리고 캐시

global Object에 전역 변수로 선언된 값은 null로 처리하거나 재할당하지 않는 한 가비지 컬렉터에 의해 수집되지 않는다. 또 캐시는 수집되지 않기 때문에 캐시사이즈가 점점 커진다면 방대한 메모리 사용을 야기시킬 수 있다.

2. 잊혀진 타이머와 콜백

setInterval, addEventListener와 같은 핸들러를 수집하지 않으면 이 핸들러에 의존하는 객체들도 수집되지 않게된다. 따라서 명시적으로 제거해주게 좋다.

3. DOM 외부에서의 참조

예를들어 JS 코드에서 특정 테이블의 셀을 참조하는 코드가 있다고 하자. 나중에 DOM에서 해당 테이블을 제거하기로 하였지만 여전히 JS코드에서 셀에대한 참조를 갖고있다. 셀은 부모요소인 table을 참조하기 때문에 테이블 전체가 메모리에 상주하게 된다. 따라서 DOM을 외부에서 참조할 때 이 점을 주의해야 한다.

4. 클로져

JS에서는 함수내부의 [[Scope]] 프라퍼티로 외부함수의 실행컨텍스트가 제거되어도 외부함수의 활성객체를 참조할 수 있고 메모리에는 외부함수의 변수들이 계속 상주하게 된다. 따라서 잘못된 클로져 사용은 메모리 사용을 발생시킬 수 있다.

Reference

  1. https://imasoftwareengineer.tistory.com/103
  2. https://soldonii.tistory.com/53
  3. [https://itstory.tk/entry/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8%EC%97%90%EC%84%9C-%EB%A9%94%EB%AA%A8%EB%A6%AC-%EB%88%84%EC%88%98%EC%9D%98-4%EA%B0%80%EC%A7%80-%ED%98%95%ED%83%9C](

Written by@[HongDongUk]
공부한 것을 소소하게 적는 블로그.

GitHubFacebook