#JavaScript #V8 #GarbageCollection

크롬에 구현된 자바스크립트 엔진인 V8은 주기적인 가비지컬렉션을 수행한다고 알려져있다. 이와 관련한 정보를 검색하다가 Daniel Khan이 작성한 글을 그대로 따라해보고, 느낀 점을 정리해본다. 해당 글은 아래 링크에서 읽어볼 수 있다.

Understanding Garbage Collection and Hunting Memory Leaks in Node.js

Resident Set

V8은 ‘Resident Set’이라는 구조로 메모리를 운용한다. JVM(Java Virtual Machine)이 메모리를 여러 세그먼트(Segment)로 나누는 것과 같이, Resident Set 구조에 의해 메모리는 Code, Stack, Heap 영역으로 나뉜다고 한다. 이 부분에 대해서는 좀 더 알아볼 필요가 있다.

process.memoryUsage()

Node.js 역시, 크롬과 마찬가지로 자바스크립트를 해석하기 위해 V8을 사용한다. Node.js는 process.memoryUsage()라는 함수를 제공하는 것으로 보인다. Resident Set의 크기, 힙(Heap)의 크기, 사용중인 힙의 크기를 알 수 있는 것으로 보인다.

Garbage Collection

기존의 C, C++에서는 프로그래머가 직접 메모리를 할당하고 해제해야하는 것과 달리 자바스크립트는 그럴 필요가 없다. 자바스크립트에서 사용되는 요소들 중, 사용되지 않는 요소들은 가비지컬렉션(Garbage Collection)에 의해 자동으로 메모리에서 제거되기 때문이다. 특히, V8은 성능적인 이유로 두 가지 가비지컬렉션을 제공한다.

  • Scavenge : 빠른, 빈번히, 그러나 불완전한 가비지 컬렉션
  • Mark-Sweep : 느린, 드물게, 그러나 완전한 가비지 컬렉션

Memory Leak

특정 요소가 미래에 사용될 일이 없음에도 불구하고 메모리를 계속 점유 중인 상황을 ‘Memory Leak’이 발생했다고 한다. Memory Leak이 누적 발생하여 할당 가능한 메모리를 초과하는 순간, 프로세스는 죽어버릴 것이다.

JavaScript’s Scope

저자는 Memory Leak을 의도적으로 유도하는 코드를 제공한다. 본 글에 해당 코드를 수록하진 않는다. 해당 코드는 replaceThing() 함수를 호출할수록 Memory Leak이 누적 발생한다. 해당 함수를 보면 originalThingtheThing과 동일한 객체를 참조하는데 이 객체는 이전의 replaceThing() 함수 호출 시에 생성된 것이다. 현재의 replaceThing() 함수 호출 시에는 theThing으로 하여금 새로이 생성된 객체를 참조하게 한다.

전역 객체인 theThingreplaceThing() 함수가 호출될 때마다 갱신되어 매번 새로이 생성된 객체를 참조한다. 따라서 originalThing, 즉, 이전의 replaceThing() 함수 호출에서 생성되었던 객체는 더 이상 사용될 일이 없으므로 메모리에서 제거되어야 한다 원래는, 가비지컬렉션에 의해서.

그럼에도 불구하고 Memory Leak이 발생하는 원인은 unused() 함수에 있다. 이를 이해하려면 자바스크립트의 유효 범위(scope)를 이해하여야 한다. 자바스크립트에서는 함수 안에 함수가, 즉, 함수가 중첩된 경우에 내부 함수가 외부 함수에 위치한 요소들을 참조할 수 있다. 결국, replaceThing() 함수 내 객체 theThing의 메소드인 someMethod()는 외부 함수에 위치한 함수 객체 unused()를 참조할 수 있다.

theThingoriginalThing은 얼핏보기에 서로 연관이 없어 보인다. 하지만 객체 theThing이 소유한 메소드 someMethod()가 함수 객체 unused를 참조하고, 이는 다시 객체 originalThing을 참조한다. 결국, originalThing은 메모리에서 제거되지 못하고 Memory Leak이 발생한다.

Closure

자바스크립트는 넓은 유효 범위 덕분에 C나 C++에서는 구현이 번거로웠던 클로저(Closure)를 손쉽게 구현할 수 있다. 클로저는 콜백으로 많이 이용된다. 넓은 유효 범위는 함수의 ‘호출’ 순간에도 ‘선언’ 당시의 문맥 속에서 동작하도록 하기 때문이다. 이 부분도 좀 더 알아볼 필요가 있다.

Inspector

웹 개발자들이 잘 알고 있을 개발자 도구, 즉, 웹 인스펙터(Inspector)는 의외로 많은 기능을 담고 있다. 본문에서는 Memory Leak이 발생한 것을 ‘인스펙터>프로파일(profile)>힙 스냅샷(Heap Snapshot)’ 기능으로 보여주고 있다. 인스펙터는 웹 개발자 뿐만 아니라, 브라우저 개발자에게도 상당한 도움되는 도구인 것 같다. 본문과 달리, 나는 인스펙터가 제공하는 자바스크립트 콘솔(console)로 Memory Leak이 발생한 것을 확인해보았다.

console memory leak