모던 JavaScript 튜토리얼을 따라가면서 정리합니다.
4.3. 가비지 컬렉션
가비지 컬렉션 기준
- 도달 가능한(reachable) 값은 메모리에서 삭제되지 않는다.
- 도달 가능한(reachable) 값은 어떻게든 접근하거나 사용할 수 있는 값을 의미한다.
- 루트(root): 태생부터 도달 가능하기 때문에 명백한 이유 없이 삭제되지 않는 값
- 현재 함수의 지역변수와 매개변수
- 중첩 함수의 체인에 있는 함수에서 사용되는 변수와 매개변수
- 전역 변수
- 루트가 참조하는 값이나 체이닝으로 루트에서 참조할 수 있는 값은 도달 가능한 값이 된다.
- 자바스크립트 엔진 내에서는 가비지 컬렉터가 끊임없이 동작한다.
- 가비지 컬렉터는 모든 객체를 모니터링하고, 도달할 수 없는 객체는 삭제한다.
연결된 객체
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function marry(man, woman) {
woman.husband = man;
man.wife = woman;
return {
father: man,
mother:woman
}
}
let family = marry({
name: "John"
}, {
name: "Ann"
});
함수 marry
(결혼하다)는 매개변수로 받은 두 객체를 서로 참조하게 하면서 ‘결혼’시키고, 두 객체를 포함하는 새로운 객체를 반환한다. 모든 객체가 도달 가능한 상태다. 이때 참조 두개를 지워보겠다.
1
2
delete family.father;
delete family.mother.husband;
John으로 들어오는 참조는 모두 사라져 도달 가능한 상태에서 벗어난다. John은 wife란 이름으로 Ann을 가리키고 있긴 하지만, 외부로 나가는 참조는 도달 가능한 상태에 영향을 주지 않는다. 외부에서 들어오는 참조만이 도달 가능한 상태에 영향을 준다. John에 저장된 데이터(프로퍼티) 역시 메모리에서 사라진다.
도달할 수 없는 섬
객체들이 연결되어 섬 같은 구조를 만드는데, 이 섬에 도달할 방법이 없는 경우, 섬을 구성하는 객체 전부가 메모리에서 삭제된다.
1
family = null;
“family” 객체와 루트의 연결이 사라지면 루트 객체를 참조하는 것이 아무것도 없게 된다. 섬 전체가 도달할 수 없는 상태가 되고, 섬을 구성하는 객체 전부가 메모리에서 제거된다.
내부 알고리즘
‘mark-and-sweep’이라 불리는 가비지 컬렉션 기본 알고리즘
- 가비지 컬렉터는 루트(root) 정보를 수집하고 이를 ‘mark(기억)’한다.
- 루트가 참조하고 있는 모든 객체를 방문하고 이것들을 ‘mark’한다.
- mark된 모든 객체에 방문하고 그 객체들이 참조하는 객체도 mark한다. 한번 방문한 객체는 전부 mark하기 때문에 같은 객체를 다시 방문하는 일은 없다.
- 루트에서 도달 가능한 모든 객체를 방문할 때까지 위 과정을 반복한다.
- mark되지 않은 모든 객체를 메모리에서 삭제한다.
루트에서 페인트를 들이부어 루트를 시작으로 참조를 따라가면서 도달 가능한 객체 모두에 페인트가 칠해진다고 생각하면 된다. 이때 페인트가 묻지 않은 객체는 메모리에서 삭제된다.
최적화 기법
- generational collection(세대별 수집): 객체를 새로운 객체와 오래된 객체로 나눈다.
- 객체 상당수는 생성 이후 제 역할을 빠르게 수행해 금방 쓸모가 없어지는데, 이를 ‘새로운 객체’로 구분한다.
- 반면 일정 시간 이상 살아남은 객체는 ‘오래된 객체’로 분류한다.
- 가비지 컬렉터는 새로운 객체를 공격적으로 메모리에서 제거하고, 오래된 객체는 덜 감시한다.
- incremental collection(점진적 수집)
- 가비지 컬렉션을 여러 부분으로 분리한 다음, 각 부분을 별도로 수행한다.
- 긴 지연을 짧은 지연 여러 개로 분산시킨다.
- idle-time collection(유후 시간 수집)
- 실행에 주는 영향을 최소화하기 때문에 CPU가 유휴 상태일 때만 가비지 컬렉션을 실행한다.