자바스크립트는 "도달 가능성"
이라는 개념을 사용해서 메모리를 관리합니다. 이 "도달 가능한"
값 이란 쉽게 말해 어떻게든 접근하거나 사용할 수 있는 혹은 사용될 가능성
이 있는 값을 의미합니다.
자바스크립트 엔진 내에선 가비지 컬렉터가 "끊임없이"
동작합니다. 가비지 컬렉터는 모든 객체를 모니터링하고, 도달할 수 없는 객체는 삭제합니다.
이 "도달 가능성"
이라는 개념은 "mark-and-sweep"
이라는 알고리즘을 사용한다고 합니다.
1.
가비지 컬렉터는 루트(root)정보를 수집하고 이를기억(mark)
합니다.
2.
루트가 참조하고 있는 모든 객체를 방문하고 이를기억(mark)
합니다.
3.
기억된 모든 객체에 방문하고 그 객체들이 참조하는 객체들도기억(mark)
합니다.
4.
루트에서부터"도달 가능한"
모든 객체를 방문할 때까지 위 과정을 반복합니다.
5.
기억(mark)
되지 않은 객체들을 메모리에서 삭제(sweep)
합니다.
그림과 같은 객체가 있다고 하면 검정 상자
안에 있는 객체들은 현재 루트(global)
에서 부터 닿는 화살표가 없기때문에 가비지 컬렉터의 대상이 됩니다.
위 그림은 마치 루트에서 페인트를 들이붓는다고 상상하면 이해하기 쉽습니다. 페인트가 닿지 않는 부분
은 가비지 컬렉터의 대상이 되어 메모리에서 영영히 사라지게 되게 됩니다.
let user = { name: "uiseop" }; // (1)
user = null; // (2)
(1)
에서 전역으로 객체를 생성하고 나면 전역 객체의 경우는 어디서든 접근 가능한 값 이기 때문에 삭제를 하지 않습니다.
(2)
에서 user가 참조하는 값이 "null"
이 되면 더이상 uiseop
객체는 도달할 수 없기 때문에 메모리에서 삭제하게 됩니다.
let user = { name: "uiseop" }; // (1)
let admin = user; // (2)
user = null; // (3)
첫번째의 예시와는 다르게 이번엔 변수admin
이 변수user
가 참조한 객체를 참조하는 방식입니다.
(3)
에서 user
가 참조하던 객체를 끊어버렸지만 여전히 admin
에서 uiseop
객체를 참조하고있기 때문에(도달되고 있음)
uiseop
은 메모리에서 삭제되지 않습니다.
가비지 컬렉터는 "끊임없이"
JS엔진에서 동작하고 있다고 했습니다. 작은 규모의 프로젝트에서는 상관 없지만 실제 사용자들이 사용하는 프로그램에서는 가비지 컬렉터가 실행에 방해되지 않도록 최적화를 해야할것이라고 생각됩니다.
최적화하는 기법으로는 3가지가 있는데 하나씩 살펴봅니다.
세대별 수집이란 객체를 "새로운 객체"
와 "오래된 객체"
로 나눕니다. 객체의 상당수는 생성 이후 곧바로 사용되어 쓸모가 없어지게 되기때문에 새로 생성된 객체를 "새로운 객체"
로 분류하고 이 객체가 일정 시간 이상 동안 쓰이고 있다면 "오래된 객체"
로 분류되게 됩니다.
새로운 객체들은 가비지 컬렌터가 메모리에서 제거하려고 눈에 불🔥
을 켜고 공격적
으로 제거하려하고 오래된 객체는 덜 감시합니다.
방문해야 할 객체가 많다면 모든 객체를 한 번에 방문하고 기억하기에는 상당한 시간이 소모됩니다. 따라서 가비지 컬렉터는 방문할 부분(컬렉션)
을 여러개로 분리한 다음, 각 부분을 별도로 수행합니다.
이는 작업을 분리하기위해, 변경사항을 추적하기 위해 개발자의 추가 작업이 필요하긴 하지만 긴 지연을 짧은 지연 여러개로 나눌 수 있다는 "장점"
이 있습니다.
앞에서 최적화를 하는 이유에 대해 이야기했습니다. 바로 실행시간에 영향을 주지 않기 위해서였는데 이를 위해 CPU가 유휴 상태
일 때에만 가비지 컬랙터가 일을 하는 방법입니다.
우리는 가비지 컬렉터가 수행하는 일, 수행에 사용되는 알고리즘, 최적화 방법을 배웠습니다. 가비지 컬렉터는 자동으로 수행되므로 개발자가 이를 억지로 실행하거나 막을 방법이 없습니다.
모던 자바스크립트 엔진은 좀 더 발전된 가비지 컬렉션 알고리즘을 사용한다고 합니다. 먼저 자바스크립트에 익숙해진 후에 엔진에 대해 더 학습하면 좋을것 같습니다.