자바스크립트 가비지 컬렉션(Garbage Collection)

이동준·2023년 7월 25일
0

자바스크립트

목록 보기
17/28

가비지 컬렉션(Garbage Collection)

자바스크립트는 객체가 생성되었을 때 자동으로 메모리를 할당하게되고, 더 이상 필요가 없을 때 자동으로 해제한다. 이걸 가비지 컬렉션이라고 하고 이러한 자동 메모리 관리는 잠재적 혼란의 원인이 되기도 하는데, 개발자가 메모리 관리에 대해 고민할 필요가 없다는 잘못된 인식을 심어줄 수 있기 때문이다.

메모리의 생존 주기

  • 필요할 때 할당된다.
  • 할당된 메모리를 사용한다.(읽기, 쓰기)
  • 더 이상 필요하지 않으면 해제한다.

두번째 부분은 모든 언어에서 명시적으로 사용된다. 그러나 첫번째와 마지막 부분은 저수준 언어에서는 명시적이며, 자바스크립트와 같은 대부분의 고수준 언어에서는 암묵적으로 작동한다.

가비지 컬렉션의 기준

자바스크립트는 도달 가능성(reachabillity)이라는 개념을 사용해 메모리 관리를 수행한다.
도달 가능한 값은 쉽게 말해 어떻게든 접근하거나 사용할 수 있는 값을 의미하는데, 도달 가능한 값은 메모리에서 삭제되지 않는다.

도달 가능한 값

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

이러한 값들을 루트(root)라고 부른다.

루트가 참조하는 값이나 체이닝으로 루트에서 참조할 수 있는 값은 도달 가능한 값이 된다.
전역 변수에 객체가 저장되어 있다고 치게되면, 객체의 프로퍼티가 또 다른 객체를 참조하고 있다면, 프로퍼티가 참조하는 객체는 도달 가능한 값이 된다. 그러면 이 객체가 참조하는 다른 모든 것들도 도달 가능하다고 여겨진다.

자바스크립트 엔진 내에선 가비지 컬렉터(garbage collector)가 끊임없이 동작한다. 가비지 컬렉터는 모든 객체를 모니터링하고, 도달 할 수 없는 객체는 삭제한다.

예시

let user = {
  name: "dongjun"
};

전역 변수 "user"{name: "dongjun"} 이라는 객체를 참조한다. dongjun 의 프로퍼티 name 은 원시값을 저장하고 있다.
user 의 값을 다른 값으로 덮어쓰면 참조가 사라진다.

user = null;

이제 dongjun 은 도달 할 수 없는 상태가 되어버렸고, dongjun 에 접근할 방법도, dongjun 을 참조하는 것도 모두 사라졌다. 가비지 컬렉터는 이제 dongjun 에 저장된 데이터를 삭제하고, dongjun 을 메모리에서 삭제한다.

두개의 참조

참조를 user 에서 admin 으로 복사했다고 가정하자.

let user = {
  name: "dongjun"
};

let admin = user;

위의 예시에서 한 것 처럼 user 의 값을 다른 값으로 덮어씌어 보면

user = null;

전역 변수 admin 을 통하면 여전히 객체 dongjun 에 접근 할 수 있기 때문에 dongjun 은 메모리에서 삭제되지 않는다.(클로저 참고) 이 상태에서 dongjun 을 다른 값(null 등)으로 덮어쓰면 dongjun 은 메모리에서 삭제될 수 있다.

연결된 객체

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 은 도달 가능한 상태에서 벗어나게 된다. 외부로 나가는 참조는 도달 가능한 상태에 영향을 주지 않기 때문에 John 은 도달 가능한 상태가 아니므로 메모리에서 제거된다. 그리고 John 에 저장된 데이터(프로퍼티) 역시 메모리에서 사라지게 된다.

Mark-and-sweep 기본 알고리즘

mark-and-sweep 이라고 불리는 가비지 컬렉션의 기본 알고리즘에 대해 알아보자.

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

최적화 기법

자바스크립트 엔진은 실행에 영향을 미치지 않으면서 가비지 컬렉션을 더 빠르게 하는 다양한 최적화 기법을 적용한다.

  • generational collection(세대별 수집) – 객체를 새로운 객체와 오래된 객체로 나눈다. 객체 상당수는 생성 이후 제 역할을 빠르게 수행해 금방 쓸모가 없어지는데, 이런 객체를 새로운 객체로 구분한다. 가비지 컬렉터는 이런 객체를 공격적으로 메모리에서 제거한다. 일정 시간 이상 동안 살아남은 객체는 오래된 객체로 분류하고, 가비지 컬렉터가 덜 감시하게 된다.

  • incremental collection(점진적 수집) – 방문해야 할 객체가 많다면 모든 객체를 한 번에 방문하고 기억하는데 상당한 시간이 소모된다. 가비지 컬렉션에 많은 리소스가 사용되어 실행 속도도 눈에 띄게 느려지는데, 자바스크립트 엔진은 이런 현상을 개선하기 위해 가비지 컬렉션을 여러 부분으로 분리한 다음, 각 부분을 별도로 수행한다. 작업을 분리하고, 변경 사항을 추적하는 데 추가 작업이 필요하긴 하지만, 긴 지연을 짧은 지연 여러 개로 분산시킬 수 있다는 장점이 있다.

  • idle-time collection(유휴 시간 수집) – 가비지 컬렉터는 실행에 주는 영향을 최소화하기 위해 CPU가 유휴 상태일 때에만 가비지 컬렉션을 실행한다.

참고 문서 : https://ko.javascript.info/garbage-collection
https://developer.mozilla.org/ko/docs/Web/JavaScript/Memory_management

0개의 댓글