자바스크립트는 눈에 보이지 않는 곳에서 메모리 관리를 수행한다.
원시값, 객체, 함수 등 우리가 만드는 모든 것은 메모리를 차지한다. 그렇다면 더는 쓸모없어지게 된 것들은 어떻게 처리될까?
자바스크립트는 도달 가능성이라는 개념을 사용해 메모리 관리를 수행한다.
‘도달 가능한' 값은 어떻게든 접근하거나 사용할 수 있는 값을 의미한다. 도달 가능한 값은 메모리에서 삭제되지 않는다.
자바스크립트 엔진 내에서는 가비지 컬렉터가 끊임없이 동작하며 모든 객체를 모니터링하고, 도달할 수 없는 객체는 삭제한다.
// user엔 객체 참조 값이 저장된다.
let user = {
name: "John"
};
그림에서 화살표는 객체 참조를 나타낸다. 전역 변수 user 는 {name: “John”} 이라는 객체를 참조하고 있다. 객체의 프로퍼티 name 은 원시값을 저장하고 있다.
user 의 값을 다른 값으로 덮어쓰면 화살표(참조) 가 사라진다.
user = null;
이제 객체는 도달할 수 없는 상태가 되었다. 객체에 접근할 방법도, 객체를 참조하는 것도 모두 사라졌으므로 가비지컬렉터는 객체에 내에 저장된 데이터를 삭제하고, 객체를 메모리에서 삭제한다.
참조를 user 에서 admin 으로 복사하면 어떻게 될까?
let user = {
name: "John"
};
let admin = user;
이 경우 둘 중 하나를 새로운 값으로 덮어써도 객체가 메모리에서 삭제되지 않는다. 그러나 참조 값을 저장하고 있는 변수 두 개가 모두 다른 값으로 덮어씌워진다면 객체는 메모리에서 삭제된다.
함수 marry 는 매개변수로 받은 두 객체를 서로 참조하게 하면서 ‘결혼'시키고, 두 객체를 포함하는 새로운 객체를 반환한다.
function marry(man, woman) {
woman.husband = man;
man.wife = woman;
return {
father: man,
mother: woman
}
}
let family = marry({
name: "John"
}, {
name: "Ann"
});
메모리 구조는 아래와 같이 나타낼 수 있다.
현재는 모든 객체가 도달 가능한 상태이다.
여기서 참조 두 개를 지운다.
delete family.father;
delete family.mother.husband;
“John” 객체에 대한 참조(들어오는 화살표)를 모두 지웠기 때문에, John 은 도달 가능한 상태가 아니게 된다. 따라서 메모리에서 제거된다. 객체 내에 저장된 프로퍼티 역시 메모리에서 제거된다.
제거 후 메모리 구조는 다음과 같다.
객체들이 연결되어 섬 같은 구조를 만들 때, 이 섬에 도달할 방법이 없는 경우 섬을 구성하는 객체 전부가 메모리에서 삭제된다.
family 가 아무것도 참조하지 않도록 만들었다. family 라는 이름으로 저장되어있는 주소값을 날려버리는 것과 같다.
family = null;
이제 메모리 내부 상태는 다음과 같아진다.
John 과 Ann 은 여전히 서로를 참조하고 있고, 외부에서 들어오는 참조 역시 갖고 있지만, 정작 이 객체를 가리키는 주소값이 사라졌기 때문에 객체와 루트의 연결이 사라지게 되고, 객체가 통째로 메모리에서 제거된다.
가비지 컬렉션 기본 알고리즘은 mark-and-sweep 이라 부른다.
이 알고리즘은 대개 이러한 단계로 구성된다.
루트에 페인트를 들이붓는다고 상상해 보자. 루트를 시작으로 도달 가능한 객체 모두에는 페인트가 칠해지고, 페인트가 묻지 않는 객체는 메모리에서 삭제된다.