메모리 생존주기는 프로그래밍 언어와 관계없이 아래 과정으로 동작된다.
하지만 1, 3번은 저수준 언어에서는 명시적이고, 대부분의 고수준 언어에서는 암묵적으로 작동한다.
음.. 요리로 예를 들어보자.
요리사가 요리를 한다고 할 때, 요리를 하기 위해 식탁에 재료들을 막 올린다. 다 쓴건 치우고 필요없는건 치우고 해야 하는데, 혼자 요리를 하다보면 신경쓰지 못하고 결국 식탁이 가득 차서 요리를 할 수 없을 수도 있다. 이 때 조수가 있다면? 옆에서 알아서 필요없는 재료는 치우고 다 쓴 재료는 버리고 할 수 있다.
프로그래밍에서도 필요한 메모리를 할당하고 사용하고 해제하는데 위의 요리와 연관지어 생각하면 이해가 쉽지 않을까...?
우리는 자바스크립트의 메모리 관리를 알아볼 것이다.
C언어 같은 저수준 언어는 메모리 관리를 위해 malloc()
과 free()
를 직접 명시함으로써 메모리를 관리한다. 하지만 자바스크립트의 경우 "가비지 컬렉터"가 객체가 생성되었을 때 자동으로 메모리를 할당하고 해당 메모리가 필요 없어지면 자동으로 해제한다.
프로그래머가 일일이 메모리 할당을 하지 않도록, 자바스크립트는 값을 초기화할 때 자동으로 메모리를 할당
let n = 123; // 정수 담기 위한 메모리 할당
let s = 'abcde'; // 문자열 담기 위한 메모리 할당
let o = {
a: 1,
b: null
}; // 오브젝트와 오브젝트에 포함된 값들을 담기 위한 메모리 할당
let a = [1, null, 'abcd']; // 배열과 배열에 담긴 값 담기 위한 메모리 할당
function f(a){
return a+2;
} // 함수 위한 할당
함수 호출의 결과로 메모리 할당이 일어난다.
let d = new Date();
let e = document.createElement('div');
메소드가 새로운 값이나 오브젝트를 할당한다.
할당된 메모리를 읽고 쓰는 것으로, 변수나 객체 속성의 값을 읽고 쓰거나 함수 호출시 함수에 인수를 전달하여 수행한다.
할당된 메모리가 더 이상 필요없을 때 할당 해제를 하는데, 해당 단계에서 대부분의 문제가 발생한다. "할당된 메모리가 더 이상 필요없을 때"를 알아내기가 어렵기 때문이다.
위에서 언급한 것처럼, 저수준 언어에서는 메모리가 필요없어질 때를 개발자가 직접 결정하고 해제하는 방식을 사용한다.
자바스크립트와 같은 고수준 언어들은 "가비지 컬렉션(GC)"이라는 자동 메모리 관리 방법을 사용한다. 가비지 컬렉터의 목적은 메모리 할당을 추적하고 할당된 메모리 블록이 더이상 필요하지 않은 시점을 판단하여 회수하는 것이다.
이러한 자동 메모리 관리 프로세스는 궁극의 방법은 아니다. 왜냐하면 어떤 메모리가 여전히 필요한지 아닌지를 판단하는 것은 비결정적 문제이기 때문이다.
"할당된 메모리가 더이상 필요없을 때"를 찾는 것은 비결정적 문제이다. 가비지 컬렉터들은 이 문제에 대한 제한적인 해결책을 구현한다.
참조(Reference)
가비지 컬렉션 알고리즘의 핵심 개념은 참조이다. A라는 메모리를 통해 B라는 메모리에 접근할 수 있다면 "B는 A에 의해 참조된다."라고 말한다.
ex) js에서 모든 객체는 prototype 객체를 암시적으로 참조하고, 그 객체의 속성을 명시적으로 참조한다.
참조-세기(Reference-counting) 가비지 컬렉션
이 알고리즘은 "더 이상 필요 없는 객체"를 "어떤 다른 객체도 참조하지 않는 객체" 라고 정의한다. 특정 객체를 참조하는 객체가 하나도 없다면 그 객체에 대해 가비지 컬렉션을 수행한다.
Mark-and-sweep 알고리즘
이 알고리즘은 "더 이상 필요 없는 객체"를 "닿을 수 없는 객체" 라고 정의한다. Mark-and-Sweep 말 그대로 무엇인가에 표시하고, 정리하는 알고리즘이다. 이 알고리즘은 roots라는 객체의 집합을 가지고 있다.(전역변수 의미 in js)
주기적으로 가비지 컬렉터는 roots로부터 시작하여 roots가 참조하는 객체들, roots가 참조하는 객체가 참조하는 객체들을 접근할 수 있는 객체라고 표시한다. 그 후 접근할 수 없는 객체에 대해 가비지 컬렉션을 수행한다.
⇒ 이 알고리즘은 "참조되지 않는 객체"는 모두 "접근할 수 없는 객체"이지만 역은 성립하지 않기 때문에 참조-세기 알고리즘보다 효율적이라고 할 수 있다.
⇒ 또한, 2012년 기준 모든 최신 브라우저들이 가비지 컬렉션에서 Mark-and-Sweep 알고리즘을 사용한다.