가비지 컬렉션

fe_sw·2022년 12월 19일
0

Javascript

목록 보기
22/22

카비지 컬렉션

C 언어같은 저수준 언어에서는 메모리 관리를 위해 malloc() 과 free()를 사용해 메모리를 수동으로 해제 한다.

반면, 자바스크립트는 객체가 생성되었을 때 자동으로 메모리를 할당하고 더 이상 필요하지 않을 때 자동으로 해제한다. 이를 가비지 컬렉션이라고 한다.

자바스크립트 엔진에 들어있는 가비지 콜렉터는 메모리 할당을 모니터링하고 할당된 메모리의 영역이 더 이상 필요하지 않은 시점을 확인하여 메모리를 회수한다.

가비지 컬렉션을 이해하기 위해서 일딴 메모리 생존주기 부터 알아야 된다.

메모리 생명 주기

    1. 필요한 메모리 할당
    1. 할당된 메모리 사용(읽기, 쓰기)
    1. 해당 메모리가 필요 없어지면 해제

필요한 메모리 할당

1) 값초기화: 자바스크립트는 값을 초기화 할 때 자동으로 메모리를 할당한다.

var n = 123; // 정수를 담기 위한 메모리 할당
var s = 'azerty'; // 문자열을 담기 위한 메모리 할당 

// 객체와 객체에 포함된 값들을 담기 위한 메모리 할당
var o = {
  a: 1,
  b: null
}; 

// 배열과 포함된 값들을 담기 위한 메모리 할당
var a = [1, null, 'abra']; 

// 함수를 담기 위한 메모리 할당
function f(a) {
  return a + 2;
} 

// 함수 표현식 또한 객체를 담기 위한 메모리를 할당
someElement.addEventListener('click', function () {
  someElement.style.backgroundColor = 'blue';
}, false);

2) 함수 호출을 통한 메모리 할당
var d = new Date(); // Date 객체 할당
var e = document.createElement('div'); // DOM element 할당

var s = 'azerty'; 
var s2 = s.substr(0, 3); 
// s2는 새로운 문자열입니다. //문자열은 불변값(immutable)이기 때문에 자바스크립트는 메모리를 새로 할당하지 않고, [0, 3]의 범위를 저장합니다. 

var a = ['ouais ouais', 'nan nan']; 
var a2 = ['generation', 'nan nan']; 
var a3 = a.concat(a2); // 4개의 원소를 가진 새로운 배열

2.할당된 메모리 사용(읽기, 쓰기)

값을 사용한다는 의미는 "할당된 메모리"를 "쓰거나 읽는다는 것"을 의미한다.

"변수나 객체 속성값을 읽고 쓸 때", "함수 호출 시 함수에 인자를 넘길 때" "값을 사용하는 것"을 의미한다.

3.할당된 메모리가 더 이상 필요 없을 때 해제

할당된 메모리를 해제하려면 할당된 메모리가 더 이상 필요하지 않은 시기를 결정해야 하는데,
이를 잘못하면 메모리 누수가 발생하게 된다. 그러므로 메모리 누수 문제는 할당한 메모리를 적절히 해제하지 못해서 발생한다고 볼 수 있다.

C, C++과 같은 로우 레벨 언어에서는 개발자가 메모리 해제 시기를 직접 결정해야 한다. 프로그램에서 할당된 메모리가 더 이상 필요하지 않을 때, 직접 메모리 할당을 해제해야 한다.

자바스크립트와 같은 몇몇 하이 레벨 언어에서는 "가비지 컬렉션(GC)"이라는 "자동 메모리 관리형식"을 활용한다.

가비지 콜렉터의 목적은 메모리 할당을 모니터링하고 할당된 메모리의 영역이 더 이상 필요하지 않은 시점을 확인하여 회수하는 것이다.

가비지 콜렉터 동작과정

참조 (Reference)

가비지 콜렉션 알고리즘의 핵심 개념은 참조이다.

A라는 메모리를 통해 (명시적이든 암시적이든) B라는 메모리에 접근할 수 있다면 "B는 A에 참조된다."라고 이야기한다.

예를 들어 자바스크립트에서 모든 객체는 prototype 객체를 암시적으로 참조하고, 그 객체의 속성을 명시적으로 참조한다

참조 세기 (Reference Counting) 가비지 컬렉션 알고리즘

이 알고리즘은 "더 이상 필요 없는 객체"를 "어떤 다른 객체도 참조하지 않는 객체"라고 정의한다.
특정 객체를 참조하는 객체가 하나도 없다면, 그 객체에 대해 가비지 컬렉션을 수행한다.


var o = {
  a: {
    b: 2
  }
};

여기서 각 객체는 모두 참조되고 있기 때문에 가비지 컬렉션이 수행될 객체는 없다.


let x = {
  a: {
    b: 2
  }
}

할당된 모든 메모리가 참조당하고 있는 상태로, 가비지가 없는 상태
전역 변수는 가비지 컬렉션의 대상이 아니므로 x에 대한 카운팅을 제외

let y = x
x = 1

변수 y에 변수 x를 대입하여 객체의 메모리 주소를 연결
변수 x에는 1을 할당하여 객체와의 연결을 끊은 상태
할당된 모든 메모리가 참조당하고 있는 상태로, 가비지가 없는 상태

let z = y.a.b
y = 'bumsu'

변수 z에 y.a.b를 대입하면 객체 b의 메모리 주소가 연결됨
변수 y에는 문자열을 할당하여 객체 a와의 연결을 끊은 상태
*객체 a를 참조하는 변수가 하나도 없기 때문에 가비지로 인식하고 메모리 공간을 해제

z = null

변수 z에 null을 대입하여 객체 b와의 연결을 끊은 상태
*객체 b가 가비지로 인식되어 메모리 공간이 해제되고, 그에 따라 숫자 2도 메모리 해제

한계: 순환 참조

이 알고리즘은 두 객체가 서로를 참조하면 문제가 발생한다. 두 객체 모두 더 이상 사용하지 않더라도 가비지 컬렉션을 수행할 수 없게 된다.


function f() {
  var x = {}; 
  var y = {};

  x.a = y;
  y.a = x;
  return 'azerty';
}

f();

위 코드에서 f 함수가 종료되고 나면, x, y에 저장한 객체는 사용되지 않으므로 가비지 컬렉션이 수행되어야 한다.

하지만 Reference-counting(참조-세기) 알고리즘에서는 두 객체 모두 참조를 가지고 있기 때문에 가비지 콜렉션이 수행되지 않는다.

Mark and Sweep 알고리즘

이 알고리즘에서는 "더 이상 필요 없는 객체"를 "닿을 수 없는 객체"로 정의한다. 이름에서 알 수 있듯이 무엇인 가에 표시(Mark)를 하고, 정리하는(Sweep) 알고리즘이다.

이 알고리즘은 roots라는 객체의 집합을 가지고 있다.(자바스크립트에서는 전역 변수들을 의미)

주기적으로 가비지 콜렉터는 roots로부터 시작하여 roots가 참조하는 객체들, roots가 참조하는 객체가 참조하는 객체들을 접근할 수 있는 객체라고 표시한다. 그 후, 접근할 수 없는 객체에 대해 가비지 컬렉션을 수행한다.

이 알고리즘은 "참조되지 않는 객체"는 모두 "접근할 수 없는 객체"이지만 역은 성립하지 않기 때문에 참조-세기 알고리즘보다 효율적이라고 할 수 있다.


function f() {
  var x = {}; 
  var y = {};

  x.a = y;
  y.a = x;
  return 'azerty';
}

f();

위 코드에서 함수 f가 리턴되고 나면, 전역 변수들에서 x, y에 담긴 객체들에 접근할 수 있는 방법이 없다. 따라서 두 객체에 대해 가비지 컬렉션이 수행될 수 있습니다

0개의 댓글