Garbage Collection

woong·2023년 2월 26일
0

면접 질문

목록 보기
7/8

메모리 관리는 왜 필요한가?

대부분 언어에서 메모리 라이프 사이클은 할당 -> 메모리 사용 -> 메모리 해제 단계를 거친다.
C와 같은 low-level 언어의 경우는 라이프 사이클을 개발자가 malloc(), free() 를 사용해 직접 관리해줘야 하지만, 자바스크립트와 같은 high-level 언어는 대부분 Garbage Collection이라는 자동 메모리 관리를 사용하기 때문에 개발자가 따로 신경쓰지 않아도 된다.

가비지 컬렉션이란?

쓸모 없어진 객체가 차지하는 메모리를 자동으로 해제하는 것.

즉, 더이상 사용하지 않는 메모리를 발견하고 이를 제거해주는 역할을 한다.

장점

  • 메모리 관리를 완벽하게 할 필요 없다.
    단점
  • 언제 동작하는지 예측하기 어렵다.
    • 객체가 쓸모 없어지는 시점에 정확히 메모리가 해제되지 않기 때문에 최적의 메모리 관리가 되지 않는다.
  • 가비지 컬렉터가 동작하는 시간이 든다.
    • 어떤 객체가 쓸모 없는지 판단하는 시간이 소요된다.

자동으로 관리해준다고 해서 개발자가 완전히 신경을 쓰지 않는다면 메모리 누수가 발생할 수도 있다. 메모리 누수란, 객체가 사용되지 않음에도 메모리에서 해제되지 않는 상태를 말한다.

가비지 컬렉션의 주요 알고리즘

1. Refence-counting

어떤 값에 대해 어디에서도 참조하지 않고 있다면 GC은 이 값을 필요하지 않은 값으로 간주하고 이 값을 제거한다.

예시

// people 변수는 해당 object를 참조한다 .
let people = {
  name: "Jane"
};
//null값을 재할당한다.
people = null;

{name: "Jane"}이라는 값은 더이상 참조되지 않기 때문에 GC에 의해 메모리가 해제된다.

// people 변수는 해당 object를 참조한다 .
let people = {
  name: "Jane"
};
//girl에 people의 참조중인 값의 주소를 할당한다.
girl = people;
//people에 null을 할당한다.
people = null;

people은 더이상 {name: "Jane"}을 참조하고 있지 않지만, girl은 참조하고 있기 때문에 해당 object는 여전히 메모리에 남아있게 된다.

// people 변수는 해당 object를 참조한다 .
let people = {
  name: "Jane"
};
//girl에 people의 참조중인 값의 주소를 할당한다.
girl = people;
//people에 null을 할당한다.
people = null;
//girl에 null을 할당한다.
girl = null;

만약 위의 코드처럼 people, girl에 null을 할당하고 나면 {name: "Jane"}를 참조하고 있는 곳이 없기 때문에 해당 값은 GC에 의해 처리된다.

하지만 reference-counting 방식은 순환참조(Circular reference)가 이루어지는 경우 메모리 누수의 요인이 된다는 문제점이 있다. 아래 예시 코드를 보자.

function couple() {
    const jane = {};
    const sam = {};

    // jane.bf는 sam을 참조한다
    jane.bf = sam;

    // sam.gf는 jane을 참조한다
    sam.gf = jane;

    return 'circular';
}

couple();

couple()함수가 호출되고 끝나서 더이상 필요한 값이 아닌데도 불구하고 서로에 대한 참조가 걸려있기 때문에 GC는 이 값들에 대한 메모리를 해제하지 않아 계속해서 메모리에 남아 있는다. 이러한 순환참조는 메모리 누수의 주된 요인이라고 할 수 있다.

IE6와 IE7은 DOM object에 reference-counting GC를 사용하는 것으로 알려져있는데, 아래의 경우는 메모리 누수를 일으키는 주된 요인이 된다. 아래에는 현실세계에서 사용될 법한 코드 예시이다.

var div;
window.onload = function() {
  div = document.getElementById('myDivElement');
  div.circularReference = div;
  div.lotsOfData = new Array(10000).join('*');
};

myDivElementcircularReference에 자기 자신의 값을 참조하고 있다. 따라서 DOM 트리에서 해당 element가 제거되었다고 하더라도 여전히 reference는 남아있기 때문에 GC는 이 값의 메모리를 해제하지 않는다. 특히나 순환참조되고 있는 값이 사이즈가 큰 데이터를 가지고 있다면 이 메모리는 해제될 수 없기 때문에 브라우저가 느려지거나 하는 이슈가 생길 수 있다.

2. Mark-and-sweep

1번 Reference-counting의 문제점으로는 불필요한 값임에도 어디에서 참조가 걸려 있다면 이에 대한 메모리를 해제하지 않는다는 문제점이 있었다. 반면에 Mark-and-sweep 알고리즘은 이 값이 참조되고 있는 값인지에 중점을 두지 않고 도달가능성(reachablitiy)에 중점을 둔다.

도달 가능성이란, 자바스크립트의 root라는 글로벌 object에서부터 시작해 참조되는가에 대한 여부이다.
다시말해 root에서부터 해당 값까지 도달이 가능한가? 에 대한 여부이다.

따라서 어떤 값에 대한 참조가 없는 경우는 당연히 도달이 불가능하기 때문에 메로리가 해제되어야 하는 값으로 여겨지고, 참조 되고 있다고 하더라고 root로부터 도달할 수 없다고 여겨진다면 처리된다.

2012년부터 모던 브라우저들은 mark-and-sweep 방식의 GC를 사용하고 있다. reference-counting 방식에서 문제가 되었던 아래의 예시를 다시 살펴보자.

function Couple() {
    const jane = {};
    const sam = {};

    // jane.bf는 sam을 참조한다
    jane.bf = sam;

    // sam.gf는 jane을 참조한다
    sam.gf = jane;

    return 'circular';
}

Couple();

예시에서 Couple() 이라는 함수 가 호출된 후 'circular'값이 return 되고 끝난 후에는 더이상 root에서 jane과 same에 도달할 수 없기 때문에 해당 값들은 GC에 의해서 메모리가 해제된다.

참고
https://ko.javascript.info/garbage-collection
https://velog.io/@bumsu0211/JavaScript-Garbage-Collection
https://sustainable-dev.tistory.com/158

profile
꾸준한 성장을 지향합니다

0개의 댓글