All Articles

가비지 컬렉션(Garbage Collection)

(가비지 컬렉션이라는 것은 보편적인 개념이지만, 이 글에서는 JavaScript를 중심으로 작성하였습니다.)

자바스크립트는 더 이상 사용되지 않을 변수와 함수를 Heap(메모리 힙)에서 제거함으로써 메모리를 효율적으로 관리한다. 이러한 역할을 수행해주는 도구를 Garbage Collector라고 한다.

메모리 생명 주기

  1. 할당(allocate): 프로그램이 사용할 수 있도록 운영체제가 메모리를 할당하는 것. JavaScript와 같이 고수준 언어에서는 개발자가 신경쓸 일이 없지만, 저수준 언어에서는 개발자가 명시적으로 처리해야한다.
  2. 사용(use): 코드 상에서 할당된 변수를 사용함으로써 읽기 및 쓰기 작업이 이루어진다.
  3. 해제(release): 프로그램에서 필요하지 않은 메모리를 OS에 되돌려주는 단계이다. 할당과 마찬가지로 저수준 언어에서는 이를 명시적으로 처리해야 한다.

마크 앤 스위프 알고리즘(Mark and Sweep Algorithms)

가비지 컬렉션 알고리즘이 의존하고 있는 주요 개념은 참조(reference)다. 만약 그것을 가리키는 참조가 하나도 없는 경우(Reachable 하지 않은 경우) 가비지컬렉션 대상(Garbage collectible)이 된다.

자바스크립트는 마크 앤 스위프 알고리즘을 사용하며, 이는 다음의 세 단계를 거친다.

  1. 가비지 컬렉터는 모든 루트로부터 완전한 목록을 만들어낸다.(루트는 전역 객체, 즉 window 객체 혹은 global 객체를 의미한다.)
  2. 그런 다음 모든 루트와 그 자식들을 검사해서 활성화 여부를 표시(Mark)한다.
  3. 활성으로 표시되지 않은 모든 메모리를 쓸어담아(Sweep) OS에 반환한다.

이 과정의 단점도 있긴 하다.

  • 어떤 메모리를 해제할지 결정하는 데 비용이 든다. 특히, 개발자가 이미 인지하고 있는 경우에도 가비지 컬렉터가 작동하므로 작업이 오버헤드가 될 수 있다.
  • 특히, 표시(Mark) 단계에서 메모리 내용이 변경되지 않아야 하기 때문에 전체 시스템의 실행이 정지된다.이 과정에서 프로그램 퍼포먼스가 저하될 수 있다.

참고 : 레퍼런스 카운팅 알고리즘(Reference Counting Algorithms)

참조 횟수를 계산하는 알고리즘이다. 이 알고리즘의 한계는 순환참조의 경우 두 객체가 적어도 한 번은 참조한 것으로 간주하므로 둘 다 가비지컬렉션 될 수 없다는 것이다. 마크 앤 스위프 알고리즘은 이 한계를 보완했다.

메모리 누수(Memory Leak)

메모리 힙이 제대로 관리되지 않을 경우, 현재는 필요가 없음에도 불구하고 메모리 공간에서 제거되지 않은 상태로 유지되는 경우를 의미한다. 사실, 그동안은 웹개발자에게 메모리 누수는 그렇게 걱정할 부분이 아니었다고 한다. 페이지 안의 링크를 클릭하게 되면, 결국 메모리상에 새 페이지를 불러오기 때문이다. 하지만 오늘날 Single Page Application이 보편화되면서 오랜 시간 동일한 페이지에서 사용이 이루어지게 되었고, 메모리 누수 방지의 필요성이 증가했다.

JavaScript에서 메모리 누수를 발생시키는 대표적인 4가지 사례를 보자.

1. 전역 변수(Global variables)를 많이 선언한 경우

  • 선언되지 않은 변수를 참조하면 이는 자동적으로 전역 변수로 생성된다. 엄격모드(Strict mode)를 사용하면 전역변수의 사용을 미리 예방 할 수 있다.
  • 명시적으로 사용된 전역변수는 회수되지 않는다. 전역변수를 사용해야 한다면, 사용된 후에는 반드시 null처리 해주는 것이 좋다.

2. 타이머 혹은 이벤트리스너를 클린업(Clean up)하지 않은 경우

  • setInterval의 경우 클린업해주지 않으면 계속 사용 중인 것으로 간주되어 가비지 컬렉터에 의해서 제거되지 못하고 메모리 공간을 계속 차지하게 된다.
  • 이벤트 리스너의 경우도 removeEventListener를 해주지 않으면 DOM요소의 수명보다 오래 남게 되어 메모리 누수가 발생한다.

3. 클로저

  • 외부 함수의 실행이 끝난뒤에도 사라지지 않고 상위 렉시컬 환경을 기억하는 것을 클로저라고 하는데, 이 기억을 위해 메모리가 필요하고 이 메모리는 참조를 제거하지 않는 한 GC에 의해 회수되지 않는다.

4. DOM을 벗어난 참조

  • DOM에서는 지워졌지만 자바스크립트에 의해 여전히 메모리를 차지하고 있는 경우를 말한다. DOM요소가 코드 내에서 변수나 객체에 의해 참조되고 있으면 DOM 내에서 삭제되어도 GC에 의해 회수되지 않는다.

[참고자료]
https://soldonii.tistory.com/53
https://ko.javascript.info/garbage-collection
https://auth0.com/blog/four-types-of-leaks-in-your-javascript-code-and-how-to-get-rid-of-them/