javascript basics #3 오브젝트 가비지 콜렉션

Jake Seo·2020년 8월 21일
1

javascript-basics

목록 보기
3/7

javascript basics #3 오브젝트 가비지 콜렉션

소스 : https://javascript.info/garbage-collection

가비지 콜렉션 개념

자바스크립트에서의 메모리관리는 우리 눈에 보이지 않고 자동으로 수행된다. 우리가 만드는 primitive, object, function은 다 메모리를 소비한다.

만일 우리가 만든 primitive, object, function과 같은 것들 중에 더이상 필요가 없어진 것들은 어떻게 될까? 자바스크립트 엔진은 어떻게 이를 알고 정리할까?

도달 가능성(Reachability)으로 판단하기

자바스크립트 메모리관리의 핵심은 도달가능성(Reachability)이다. 간단히 말하자면 "도달 가능한(Reachable)" 값들은 접근 가능하거나 언젠가 써먹을 수 있다. 도달 가능한(Reachable) 값들은 메모리에 저장된다고 보장할 수 있다.

  1. 본질적으로 접근 불가능한 값이지만 확실한 이유에 의해 삭제될 수 없는 값들이 있다. 이를테면,
  • 지역 변수들과 현재 함수의 파라미터들
  • 중첩된 호출의 현재 체인에 있는 다른 함수들을 위한 파라미터와 변수들
  • 전역 변수들
  • 기타 내부적인 것들...
    이러한 값들은 roots라고 불린다.
  1. 만일 root로부터 참조에 의해 혹은 참조의 체인에 의해 접근 가능하면, 도달 가능하다(Reachable)고 판단한다.

    이를테면, 전역 영역에 오브젝트가 하나 있는데, 다른 오브젝트를 참조하는 프로퍼티를 갖고 있다면, 그 다른 오브젝트는 접근 가능한(Reachable) 오브젝트라고 여겨질 것이다.

이러한 자바스크립트 엔진 내부의 백그라운드 프로세스를 garbage collector라고 부른다. garbage collector는 모든 오브젝트를 모니터링한다. 그리고 접근 불가능한(Unreachable) 오브젝트가 생길 때 그 오브젝트를 제거해준다.

간단한 예제

let person = {
  name: "Jake Seo"
};

위와 같이 만들면 "name"이라는 프로퍼티는 "Jake Seo"라는 primitive를 가리킨다.

person = null;

만일 우리가 위와 같이 person 오브젝트가 참조하는 것을 null로 바꾼다면, 기존에 만들었던 person 오브젝트는 어디에서도 참조되지 않는다.

이 때, 자바스크립트 엔진의 garbage collector는 참조되지 않는 오브젝트를 삭제하고 메모리공간을 확보할 것이다.

두 개의 참조가 있을 때

우리가 person이라는 참조의 주소를 developer라는 참조로 옮겼을 때를 가정해서 생각해보자.

let person = {
  name: "Jake Seo"
};

let developer = person;

위의 간단한 예제의 경우와 똑같이

person = null;

이렇게 personnull을 할당한다면, garbage collection은 일어날까? 정답은 안 일어난다.

위의 코드 결과와 같이 아직 developer라는 변수가 오브젝트를 가리키고 있기 때문이다. 만일 developer에도 똑같이 null을 할당한다면 그 때는 garbage collection이 일어날 것이다.

내부적으로 연결된 오브젝트

여기 더욱 복잡한 예제가 있다.

function marry(man, woman) {
  woman.husband = man;
  man.wife = woman

  return {
    father: man,
    mother: woman
  }
}

let family = marry({
  name: "John"
}, {
  name: "Ann"
});

함수 marry는 두 오브젝트가 서로를 참조하게 만들고, 두 오브젝트를 포함한 하나의 새로운 오브젝트를 만든다.

현재까지는 모든 오브젝트가 접근 가능한(Reachable) 상태이다.

다음 두가지 참조를 지우면 어떻게 될까?

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

이렇게 되면 { name : "John" } 오브젝트를 참조하는 오브젝트가 사라진다. 참조되지 않는 오브젝트는 자동으로 삭제된다.

밖으로 나가는 참조와 상관없이 내부로 들어오는 참조가 사라지면 해당 오브젝트는 더 이상 접근 불가능한(Unreachable) 오브젝트로 가정하고 삭제된다.

garbage collection이 끝난 결과는 다음과 같다.

접근 불가능한 섬

내부적으로 서로 참조하는 오브젝트가 한번에 접근 불가능하게 변할 수도 있다. 다음의 코드를 수행했다고 가정해보자.

let family = null;

기존에 family가 참조하던 오브젝트의 메모리 주소는 어디에서도 접근 불가능한(Unreachable) 상태가 되므로 전부 삭제된다.

이러한 예제는 접근 가능성(Rechability)의 개념이 얼마나 중요한지를 설명한다.

내부적인 알고리즘

가장 기본적인 garbage collection 알고리즘은 "mark-and-sweep(체크하고 훑기)" 이라 불리운다.

다음의 garbage collection 단계들이 정기적으로 행해진다.

  • garbage collector는 최상위에 있는 root들에 접근하고 그들을 전부 "mark"한다.
  • garbage collectorroot로부터 연결되는 모든 참조들에 방문하고 "mark"한다.
  • garbage collector"mark"된 모든 오브젝트에 방문하고 그 오브젝트들의 참조를 "mark" 한다. 방문된 모든 오브젝트는 2번 이상 방문하지 않도록 반드시 기억한다.
  • 모든 접근 가능한(reachable) 참조에 방문할 때까지 계속 방문한다.
  • "mark"되지 않은 오브젝트는 삭제한다.

root로부터 아주 커다란 페인트를 쏟는 장면을 상상할 수 있다. 페인트가 묻지 않은 오브젝트들은 지워진다.

그리고 자바스크립트 엔진은 이외에도 많은 최적화 방법을 통해 자바스크립트 코드 수행에 영향이 없도록 이 과정을 더욱 빠르게 수행한다.

다음과 같은 몇가지 최적화 방법이 있다.

  1. Generation collection(세대 구분하기)
  • 오브젝트들은 두가지 셋으로 나뉘는데, "새 오브젝트""오래된 오브젝트"로 나뉜다. 많은 오브젝트가 나타나고 주어진 일을 하고 빠르게 죽는다. 이러한 오브젝트들은 매우 공격적으로 정리될 수 있다. 오랫동안 살아남은 오브젝트는 "오래된 오브젝트" 분류에 속하게 되고, 가비지 콜렉션에 의한 체크를 덜 받는다.
  1. Incremental collection
  • 만일 많은 오브젝트가 존재하고 한번에 모든 오브젝트를 "mark" 하려한다면, 실행하는데에 어느정도 시간이 걸리고 어느정도는 눈에 보이는 딜레이가 발생할 것이다. 그래서 자바스크립트 엔진은 garbage collection을 한번에 하는 것이 아니라 여러번 나눠서 진행한다. 나눠진 garbage collection 과정은 하나하나씩 독립적으로 수행된다. 이러한 과정은 변화를 추적하기 위해 추가적인 기록이 요구되지만, 큰 하나의 딜레이 대신에 작은 여러번의 딜레이를 가질 수 있다.
  1. idle-time collection
  • garbage collector는 실행에 미치는 영향을 줄이기 위해서, CPU가 휴지기(idle)일 때 만 동작한다.

garbage collection 알고리즘에는 다른 최적화들도 많이 존재한다. 많은 자바스크립트 엔진들이 있고 각각의 자바스크립트 엔진들은 다르게 구현되어 있다. 그리고 훨씬 더 중요한 것은 엔진이 개발되면서 garbage collection의 방법은 바뀌기 마련이다. 그러므로 지식이 필요하지 않은 상태에서 미리 깊게 공부하는 것은 때론 도움이 되지 않을 수 있다.

요약

  • garbage collection은 자동으로 동작한다. 우리가 강요하거나 막을 수 없다.
  • 오브젝트는 접근 가능한(Reachable) 한 계속 메모리에 남아있는다.
  • 참조되는 것은 접근 가능한(Reachable)한 것과는 다르다. 내부적으로 서로를 참조하는 오브젝트가 있다 하더라도 root로부터 접근이 불가능하다면 위의 접근 불가능한 섬의 예시처럼 될 수 있다.

현대 자바스크립트 엔진에는 더욱 진보된 알고리즘이 사용되고 있다.

low-level 프로그래밍에 익숙하다면, V8 garbage collector에 대한 글인 A tour of V8: Garbage Collection에 더욱 자세한 내용이 있다.

V8 blog도 역시 메모리 관리법에 대한 글을 쓴다. V8 엔지니어인 Vyacheslav Egorov의 블로그도 도움이 될 것이다.

low-level 최적화에 관심이 갈 때 garbage collection 최적화에 대한 글을 읽으면 도움이 될 것이다.

profile
풀스택 웹개발자로 일하고 있는 Jake Seo입니다. 주로 Jake Seo라는 닉네임을 많이 씁니다. 프론트엔드: Javascript, React 백엔드: Spring Framework에 관심이 있습니다.

0개의 댓글