본문 바로가기
Programming/JavaScript

Javascript 메모리 관리

by 코뮤(commu) 2023. 7. 4.
728x90
반응형

C 언어에서는 메모리 관리를 위해 malloc 과 free 를 이용해 할당하고, 해제하는 것을 개발자가 명시적으로 사용해줘야했다.

하지만 자바스크립트는 객체가 생성되었을 때 자동으로 메모리를 할당하고 더 이상 필요하지 않을 때 자동으로 해제한다.

(사실 거의 대부분의 고수준 언어들이 암묵적으로 동작한다.) 많이 들어봤던 가비지 컬렉션에 대한 내용이다.

 

 

 

자바스크립트 메모리 할당

 

1. 값 초기화

 

자바스크립트는 값을 선언할 때 자동으로 메모리를 할당한다.

 

2. 함수 호출을 통한 할당

 

let date_time = new Date()

위와 같이 함수 호출의 결과 메모리 할당이 일어나기도 한다.

메소드가 새로운 값이나 오브젝트를 할당하는 경우도 있다.

 

 

자바스크립트 메모리 해제

 

C는 개발자가 더이상 메모리가 필요없을 때 free 를 통해 직접 제어해서 결정할 수 있었다.

그런데, JS 는 어떻게 "할당된 메모리가 더 이상 필요없을 때"를 알아낼 수 있을까?

 

가비지 콜렉션이라는 자동 메모리 관리 방법이 있다.

가비지 콜렉터가 메모리 할당을 추적하고 이 메모리가 더 이상 필요하지 않게 되었는가? 를 판단하여 할당된 메모리를 회수한다.

MDN 문서에 따르면 이러한 자동 메모리 관리 프로세스가 궁극의 방법은 아니라고 한다. (필요/불필요의 판단 문제는 비결정적 문제이기 때문에)

가비지 콜렉션에 대해 좀 더 깊게 알아보자.

 

 

가비지 콜렉션

 

참조

 

가비지 콜렉션 알고리즘의 핵심 개념은 참조이다.

 

Reference-counting 가비지 콜렉션

 

가장 단순하게 구현된 알고리즘으로,

어떤 다른 오브젝트도 참조하지 않는 오브젝트를 "더 이상 필요 없는 오브젝트" 라고 여긴다.

이 오브젝트를 "가비지" 라 칭하며, 이를 참조하는 다른 오브젝트가 하나도 없는 경우 수집이 가능해진다.

 

let x = {
	a: {
    	b: 2
    }
}

// 2개의 오브젝트가 생성되었고, 하나의 오브젝트는 다른 오브젝트의 속성으로 참조 된다.
// 나머지 하나는 'x' 변수에 할당되었다.
// 가비지 콜렉션 수행될 메모리는 하나도 없다.

let y = x; // y 변수는 위의 오브젝트를 참조하는 두번째 변수이다.
x = 1; // y 변수가 위의 오브젝트를 참조하는 유일한 변수가 되었다!

let z = y.a; // 위 오브젝트의 'a' 속성을 참조했다.
			// 이제 'y.a'는 두 개의 참조를 가진다.
            // 'y'가 속성으로 참조하고 'z'라는 변수가 참조한다.
            
y = 'mozilla'; // 참조하는 유일한 변수였던 y 에 다른 값을 대입했다.
				// 오브젝트의 'a' 속성이 여전히 'z'변수에 의해 참조되므로 메모리 해제 불가능
                
z = null;	// 'z' 변수에 다른 값을 할당했다.
			// 드디어 가비지 콜렉션이 수행된다.

 

 

순환 참조는 한계가 명확하다.

아래 예제를 확인해보자.

 

 

function f() {
  var x = {};
  var y = {};
  x.a = y;         // x는 y를 참조합니다.
  y.a = x;         // y는 x를 참조합니다.

  return "azerty";
}

f();

 

두 객체가 서로 참조하는 속성으로 생성되어 순환 구조를 생성한다.

함수 호출이 완료되면 이 두 객체는 스코프를 벗어나며,

이 시점에서 두 객체는 필요하지 않으므로 할당된 메모리는 회수되어야 마땅하다.

 

하지만 두 객체가 서로를 참조하고 있기때문에

두 객체 다 가비지 컬렉션의 대상으로 표시하지 않는다.

이게 곧... 메모리 누수로 이어진다!

 

Mark-and-sweep 알고리즘

 

자바스크립트에서 root 는 전역 오브젝트이다.

주기적으로 가비지 콜렉터는 roots 에서 시작해서 roots 가 참조하는 오브젝트들,

roots가 참조하는 오브젝트가 참조하는 오브젝트.. 등을 찾는다.

 

이 알고리즘은 reference-counting 보다 개선된 형태라고 할 수 있다.

바로 위의 예시에서 함수 호출이 리턴되고 나서 두 객체들은

더이상 전역 객체에서 참조하고 있는 어떤 리소스에서도 참조하지 않는다.

결국 두 객체들은 가비지 컬렉터가 닿을 수 없는 것으로 판명되고 할당되었던 메모리를 회수하게 되는 것이다.

 

현재 모든 최신 엔진은 이 가비지 콜렉션을 제공한다.

 

 

엔진의 메모리 모델 설정하기

 

가용 힙 메모리의 최대량은 아래와 같이 설정할 수 있다.

node --max-old-space-size=6000 index.js

 

 

메모리 관리를 돕는 데이터 구조

 

자바스크립트는 가비지 컬렉터 API 를 직접적으로 노출하지는 않고,

간접적으로 관리할 수 있는 몇 개의 데이터 구조를 제공한다.

 

바로 WeakMap 과 WeakSet 이다.

 

 

 

 

출처 : https://developer.mozilla.org/ko/docs/Web/JavaScript/Memory_management

728x90
반응형