📘 Frontend/Javascript

Javascript - Garbage Collection

신건우 2023. 7. 24. 06:22

Garbage Collection

자바스크립트는 눈에 보이지 않는 곳에서 메모리 관리를 수행합니다.

원시값, 객체, 함수 등 우리가 만드는 모든 것은 메모리를 차지합니다.

그렇다면 더는 쓸모 없어지게 된 것들은 어떻게 처리될까요?

지금부턴 자바스크립트 엔진이 어떻게 필요 없는 것을 찾아내 삭제하는지 알아보겠습니다.


자바스크립트는 도달 가능성(reachability) 이라는 개념을 사용해 메모리 관리를 수행합니다.

‘도달 가능한(reachable)’ 값은 쉽게 말해 어떻게든 접근하거나 사용할 수 있는 값을 의미합니다.

도달 가능한 값은 메모리에서 삭제되지 않습니다.


아래 소개해 드릴값들은 그 태생부터 도달 가능하기 때문에, 명백한 이유 없이는 삭제되지 않습니다.

  • 현재 함수의 지역 변수와 매개변수
  • 중첩 함수의 체인에 있는 함수에서 사용되는 변수와 매개변수
  • 전역 변수
  • 기타 등등

이런 값은 root 라고 불립니다.


루트가 참조하는 값이나 체이닝으로 루트에서 참조할 수 있는 값은 도달 가능한 값이 됩니다.

전역 변수에 객체가 저장되어있다고 가정해 봅시다.

이 객체의 프로퍼티가 또 다른 객체를 참조하고 있다면, 프로퍼티가 참조하는 객체는 도달 가능한 값이 됩니다.

이 객체가 참조하는 다른 모든 것들도 도달 가능하다고 여겨집니다. 자세한 예시는 아래에서 살펴보겠습니다.


UnReachable Property

아주 간단한 예시를 들어보겠습니다.

let user = {
    name: "Kim"
};

user = null; // Kim = Unreachable

전역 변수인 user는 name: "Kim" 이라는 객체를 참조합니다.

Kim의 프로퍼티 name은 원시값을 저장하고 있기 때문에 객체 안에 표현했습니다.

user 변수를 선언할 시점엔 Kim에 도달할 수 있었지만 user 변수에 null을 할당하면서 참조도 사라집니다.

참조가 사라졌으니 Kim에 도달할 수 있는 방법이 없기 때문에 Garbage Collection의 제거 대상이 됩니다.

그럼 이제 Garbage Collection이 Kim에 저장된 데이터를 삭제하고 메모리에서도 삭제합니다.


참조가 2개인 객체의 Garbage Collection 제거 대상

let user = {
    name: "Kim"
};

let admin = user;
user = null;

전역 변수 admin을 통하면 여전히 객체 John에 접근할 수 있기 때문에 John은 메모리에서 삭제되지 않습니다.

이 상태에서 admin을 다른 값(null 등)으로 덮어쓰면 John은 메모리에서 삭제될 수 있습니다.


연결된 객체

이제 가족관계를 나타내는 예시를 보겠습니다.

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

    return {
        father: man,
        mother: woman
    }
}

let family = marry(
    { name: "Kim" },
    { name: "Lee" }
);

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

함수 marry(결혼하다)는 매개변수로 받은 두 객체를 서로 참조하게 하면서 '결혼’시키고, 두 객체를 포함하는 새로운 객체를 반환합니다.


삭제한 2개의 참조 중 하나만 지웠다면 모든 객체가 여전히 도달 가능한 상태였을 겁니다.

하지만 참조 2개를 다 지우면 Kim으로 들어오는 참조가 모두 사라져 Unreachable 상태가 됩니다.

위 코드에선 2개의 참조를 모두 다 지웠기 떄문에 Garbage Collection의 대상이 되고 남은건 family.mother 이 됩니다.


Garbage Collector 내부 알고리즘

'mark-and-sweep’이라 불리는 가비지 컬렉션 기본 알고리즘에 대해 알아봅시다.

'가비지 컬렉션’은 대개 다음 단계를 거쳐 수행됩니다.

  • 가비지 컬렉터는 루트(root) 정보를 수집하고 이를 ‘mark(기억)’ 합니다.
  • 루트가 참조하고 있는 모든 객체를 방문하고 이것들을 ‘mark’ 합니다.
  • mark 된 모든 객체에 방문하고 그 객체들이 참조하는 객체도 mark 합니다.
  • 한번 방문한 객체는 전부 mark 하기 때문에 같은 객체를 다시 방문하는 일은 없습니다.
  • 루트에서 도달 가능한 모든 객체를 방문할 때까지 위 과정을 반복합니다.
  • mark 되지 않은 모든 객체를 메모리에서 삭제합니다.

요약

  • 가비지 컬렉션은 엔진이 자동으로 수행하므로 개발자는 이를 억지로 실행하거나 막을 수 없습니다.
  • 객체는 도달 가능한 상태일 때 메모리에 남습니다.
  • 참조된다고 해서 도달 가능한 것은 아닙니다. 서로 연결된 객체들도 도달 불가능할 수 있습니다.

모던 자바스크립트 엔진은 좀 더 발전된 가비지 컬렉션 알고리즘을 사용합니다.

어떤 알고리즘을 사용하는지 궁금하다면 ‘The Garbage Collection Handbook: The Art of Automatic Memory Management’(저자 – R. Jones et al)를 참고하시기 바랍니다.

저수준(low-level) 프로그래밍에 익숙하다면, A tour of V8: Garbage Collection을 읽어보세요. V8 가비지 컬렉터에 대한 자세한 내용을 확인해 볼 수 있습니다.