Gidhub BE Developer

Javascript Garbage Collection

2018-12-01
goodGid

  • 자바스크립트의 메모리 관리는 우리에게는 보이지 않게 자동으로 실행된다.

  • 우리가 원시타입의 변수나 혹은 객체, 함수를 선언할때도 모두 메모리를 사용한다.

  • 만약에 이러한 것들이 더이상 필요없게 된다면?

  • 자바스크립트 엔진은 어떻게 이것들을 찾아내어 삭제할까?

  • 그전에 JavaScript의 데이터 타입에 대해 학습하고 가자.


접근 가능성(Reachability)

  • 자바스크립트 메모리 관리의 주요 개념은 접근 가능성이다.

  • 간단하게 말하면 접근 가능한 값은 어떻게든 엑세스가 가능하거나 사용할 수 있는 값임을 뜻한다.

  • 그렇기 때문에 이들은 메모리에 유지되는 것을 보장 받는다.

  • 자바스크립트 엔진에는 백그라운드 프로세스로 동작하는 가비지 콜렉션가 있다.

  • 이것은 모든 객체들을 모니터링 하며 그것들이 접근 불가능하게 되었을 때 삭제하는 작업을 수행한다.


예제

// user는 객체에 대한 참조를 가지고 있습니다.
let user = {
  name: "John"
};

  • 여기에 표시된 화살표는 객체 참조를 나타냅니다.

  • 그런데 여기서 만약 user의 값을 덮어쓰게 되면 참조를 잃게 된다.

user = null;

  • 이제 헤당 Object에는 접근이 불가능하게 되었다.

  • 여기에 접근할 방법은 없으며 아무도 참조하지 않기 때문에 가비지 콜렉터는 데이터를 회수하고 메모리를 비우게 된다.


두개의 참조

  • 이번에는 user가 가리키는 객체를 admin도 동시에 가리키도록 해보자.
let user = {
  name: "John"
};
 
let admin = user;

  • 그리고 객체를 가리키던 user 변수의 값을 덮어쓴다.
user = null;
  • 하지만 여전히 admin 변수가 객체를 참조하고 있기 때문에 메모리에 유지된다.

  • 만약 admin 변수도 초기화 한다면 객체는 그 때 메모리 회수를 당할 것이다.


상호 연결된 객체

  • 좀 더 복잡한 예제를 살펴보자.
function marry(man, woman) {
  woman.husband = man;
  man.wife = woman;
 
  return {
    father: man,
    mother: woman
  }
}
 
let family = marry({
  name: "John"
}, {
  name: "Ann"
});
  • marry는 두개의 객체를 서로 참조하게 하고 이 둘을 참조하고 있는 새로운 객체를 반환하는 함수이다.

  • 메모리 구조의 결과는 다음과 같다.

  • 모든 객체는 서로 접근 가능하게 되었다.

  • 이제 두개의 참조를 삭제보자.

delete family.father;
delete family.mother.husband;

  • 이 두개의 참조 중 하나만 삭제할 경우에는 여전히 모든 객체가 접근 가능하므로 객체가 삭제되기에 충분하지 않다.

  • 하지만 두개의 참조 모두를 삭제할 경우 John이라는 프로퍼티를 갖는 객체는 메모리 회수가 된다.

  • John 객체가 가리키는 바깥을 향하는 참조는 상관없다.

  • GC의 관심은 안으로 들어오는 참조의 유무이다.

  • 가비지 콜렉션이 동작한 이후에는 다음과 같이 됩니다.


접근 불가능한 객체 집합

  • 외부에서 접근 불가능한 자기들끼리만 상호 참조하여 만들어진 완벽한 형태의 객체 집합도 메모리에서 삭제 가능하다.

  • 소스코드는 위와 동일하다고 가정하고 다음의 코드를 실행해보면 다음과 같다.

  • 이 예제는 접근 가능성에 대한 매우 중요한 개념을 보여주는 데모이다.

  • John과 Ann은 연결되어 있다.

  • 그 둘은 안/밖으로 연결되는 링크들 모두를 가지고 있지만 family라는 루트(Root)와의 연결이 끊어진 상태이다.

  • 그러므로 이 객체 집합은 접근 불가능하며 메모리 회수가 될 것이다.


내부 알고리즘

  • 기본적인 가비지 콜렉션의 알고리즘은 마크 앤 스윕(Mark-and-sweep)이라고 불린다.

  • 일반적으로 가비지 콜렉션은 다음의 과정을 거친다.

    • 가비지 콜렉터는 루트를 획득하여 그들을 마크(기억)한다.

    • 그리고 그들이 참조하고 있는 모든 것들에 방문하여 마크한다.

    • 그리고 마크한 모든 객체에 방문하여 그들의 참조 역시 마크한다.
    • 모든 객체들을 기억하고 나면 미래에는 같은 객체를 두번 방문하지 않는다.

    • 루트로부터 접근 가능한 방문하지 않은 참조가 있다면 계속해서 반복한다.

    • 마크되지 않은 모든 객체는 삭제된다.
  • 예를 들어 다음과 같은 객체의 구조가 있다고 해보자.

  • 우리는 오른편에 접근 불가능한 객체들을 발견할 수 있다.

  • 이제 가비지 콜렉터가 진행하는 마크 앤 스윕 과정이 이것을 어떻게 다루는지 보자.

  • 다음은 루트로부터 첫번째 과정을 거친 결과이다.

  • 이후에 그들의 참조들도 마크한다.

  • 그리고 그들의 참조도 반복한다.

  • 이제 방문할 수 없는 객체들은 접근 불가능한것으로 간주되어 삭제된다.

  • 이것이 가비지 콜렉터가 동작하는 개념이다.

  • 자바스크립트 엔진은 어플리케이션의 실행에 영향을 주지 않고 빠르게 수행되도록 하기 위해 많은 최적화 옵션을 적용하고 있다.


Reference


Index