본문 바로가기

언어(JS,TS)/그룹스터디

그룹스터디 [CS : 가비지 컬렉션?, 가비지 컬렉션 기능을 가진 언어]

자바스크립트의 메모리 관리

C 언어같은 저수준 언어에서는 메모리 관리를 위해 malloc() 과 free()를 사용합니다. 반면, 자바스크립트는 객체가 생성되었을 때 자동으로 메모리를 할당하고 더 이상 필요하지 않을 때 자동으로 해제합니다(가비지 컬렉션).

출처 : 위키피디아

 

메모리 생존주기

메모리 생존주기는 프로그래밍 언어와 관계없이 비슷합니다.

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

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

 

 

가비지 컬렉션

가비지 컬렉션은 프로그램에서 더 이상 사용하지 않는 메모리를 자동으로 정리하는 것입니다.

대표적인 가비지 컬렉션의 방법으로는 레퍼런스 카운팅트레이싱이 있습니다.

 

 

레퍼런스 카운팅

레퍼런스 카운팅은 한 객체를 참조하는 변수의 수를 추적하는 방식입니다. 객체를 참조하고 있던 변수의 값이 바뀌거나, 변수 스코프를 벗어나면 레퍼런스 카운트가 줄어드는데, 이때 레퍼런스 카운트가 0이 되면, 그 객체와 관련한 메모리는 비울 수 있습니다.

이러한 가비지 컬렉션 기능을 가진 언어(혹은 엔진)는 자바, C#, 자바스크립트 등이 있습니다.

 

참조-세기(Reference-counting) 가비지 콜렉션

참조-세기 알고리즘은 가장 소박한 알고리즘입니다. 이 알고리즘은 "더 이상 필요없는 오브젝트"를 "어떤 다른 오브젝트도 참조하지 않는 오브젝트"라고 정의합니다. 이 오브젝트를 "가비지"라 부르며, 이를 참조하는 다른 오브젝트가 하나도 없는 경우, 수집이 가능합니다.

예제

var x = {
  a: {
    b: 2
  }
};
// 2개의 오브젝트가 생성되었습니다. 하나의 오브젝트는 다른 오브젝트의 속성으로 참조됩니다.
// 나머지 하나는 'x' 변수에 할당되었습니다.
// 명백하게 가비지 콜렉션 수행될 메모리는 하나도 없습니다.


var y = x;      // 'y' 변수는 위의 오브젝트를 참조하는 두 번째 변수입니다.

x = 1;          // 이제 'y' 변수가 위의 오브젝트를 참조하는 유일한 변수가 되었습니다.

var z = y.a;    // 위의 오브젝트의 'a' 속성을 참조했습니다.
                // 이제 'y.a'는 두 개의 참조를 가집니다.
                // 'y'가 속성으로 참조하고 'z'라는 변수가 참조합니다.

y = "mozilla";  // 이제 맨 처음 'y' 변수가 참조했던 오브젝트를 참조하는 오브젝트는 없습니다.
                // (역자: 참조하는 유일한 변수였던 y에 다른 값을 대입했습니다)
                // 이제 오브젝트에 가비지 콜렉션이 수행될 수 있을까요?
                // 아닙니다. 오브젝트의 'a' 속성이 여전히 'z' 변수에 의해 참조되므로
                // 메모리를 해제할 수 없습니다.

z = null;       // 'z' 변수에 다른 값을 할당했습니다.
                // 이제 맨 처음 'x' 변수가 참조했던 오브젝트를 참조하는
                // 다른 변수는 없으므로 가비지 콜렉션이 수행됩니다.
Copy to mozilla

단점: 순환 참조

순환 참조를 다루는 일에는 한계가 있습니다. 다음 예제에서는 두 객체가 서로 참조하는 속성으로 생성되어 순환 구조를 생성합니다. 함수 호출이 완료되면 이 두 객체는 스코프를 벗어나게 될 것이며, 그 시점에서 두 객체는 불필요해지므로 할당된 메모리는 회수되어야 합니다. 그러나 두 객체가 서로를 참조하고 있으므로, 참조-세기 알고리즘은 둘 다 가비지 컬렉션의 대상으로 표시하지 않습니다. 이러한 순환 참조는 메모리 누수의 흔한 원인입니다.

 

 

 

트레이싱

포인터추적관리중 트레이싱의 경우 한 객체에 flag를 두고, 가비지 컬렉션 사이클마다 flag에 표시 후 삭제하는 mark and sweep 방식입니다.

 

'mark-and-sweep’

이라 불리는 가비지 컬렉션 기본 알고리즘

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

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

다음과 같은 객체 구조가 있다고 해봅시다.

 

 ‘mark-and-sweep’ 알고리즘  처리 방식

  1. 첫 번째 단계에선 루트를 mark 
  2. 이후 루트가 참조하고 있는 것들을 mark (한번 방문한 객체는 전부 mark 하기 때문에 같은 객체를 다시 방문하는 일은 없음)
  3. 도달 가능한 모든 객체를 방문할 때까지, mark 한 객체가 참조하는 객체를 계속해서 mark
  4. 방문할 수 없었던 객체를 메모리에서 삭제

장점 : 순환 참조는 이제 문제가 되지 않습니다.

첫 번째 예제에서 함수가 반환되고 나서 두 오브젝트는 닿을 수 없습니다. 따라서 가비지 콜렉션이 일어납니다.

두 번째 예제에서도 마찬가지입니다. div 변수와 이벤트 핸들러가 roots로 부터 닿을 수 없으면 순환 참조가 일어났음에도 불구하고 가비지 콜렉션이 일어납니다. (역자2: div 선언을 블럭안에다 넣어야 됩니다.(테스트는 못 해봤습니다.))

단점 : 수동 메모리 해제.

어떤 메모리를 언제 해제할지에 대해 수동으로 결정하는 것이 편리할 때가 있습니다. 그리고 수동으로 객체의 메모리를 해제하려면, 객체 메모리에 도달할 수 없도록 명시하는 기능이 있어야 합니다.

 

 

 

reference

https://ko.javascript.info/garbage-collection

 

가비지 컬렉션

 

ko.javascript.info

https://ko.wikipedia.org/wiki/%EC%93%B0%EB%A0%88%EA%B8%B0_%EC%88%98%EC%A7%91_(%EC%BB%B4%ED%93%A8%ED%84%B0_%EA%B3%BC%ED%95%99) 

 

쓰레기 수집 (컴퓨터 과학) - 위키백과, 우리 모두의 백과사전

쓰레기 수집(garbage collection 가비지 컬렉션[*], GC)은 메모리 관리 기법 중의 하나로, 프로그램이 동적으로 할당했던 메모리 영역 중에서 필요없게 된 영역을 해제하는 기능이다. 영어를 그대로 읽

ko.wikipedia.org