NATIVE

settings_applicationsObject

NATIVE

Editing
  • account_tree
  • bug_report

ObjectWeakMap

Map 객체의 역할과 기본적으로 동일하지만 메모리 효율을 위한 설계가 반영된 객체이다.

설명

가비지 컬렉터(garbage collector)

객체가 생성이 되면 메모리의 공간을 사용하며 더 이상의 객체 사용이 없을 때는 사용했던 메모리 공간을 비워야 한다. 시스템적으로 이러한 역할을 담당하는 알고리즘이 있는 데 이것을 가비지 컬렉터(GC: garbage collector)라 한다. 가비지 컬렉터는 더 이상 참조되지 않는 객체들을 수집하여 제거한다. 여기서 참조의 형태는 다양하다. 실제도 사용되는 참조가 있고 지금 사용되지 않더라도 다른 데이터에서 참조를 유지한다면 그 객체는 참조되고 있는 것이며 향후 사용될 것이라는 예상으로가비지 컬렉터에 의해 수집되지 않는다.

정말로 참조되는 객체일까?

예를 들어서 다음과 같은 코드가 있다고 하자.

let obj = { name: "I am Obj" };

변수 obj는 객체가 생성되고 생성된 객체의 할당된 메모리상의 주소 참조만을 갖는다. 이어서 다음과 같은 코드가 추가되었다고 하자.

let obj = { name: "I am Obj" };
let arr = [obj];
obj = null;

변수 arrArray 객체의 인스턴스이며 원소로 객체를 참조하는 obj를 갖는다. 그리고 이어 그 objnull을 대입해 객체와의 참조를 끊었다. 그러면 { name: "I am Obj" }는 참조되지 않는 객체일까? 그렇지 않다. 여전히 arr[0]에 의해서 참조된다.

let obj = { name: "I am Obj" };
let arr = [obj];
obj = null;

console.log(arr[0].name); // "I am Obj"

따라서 객체는 메모리상에서 그대로 남아있다. 비록 지금 arr[0]이 사용되지 않더라도 말이다.

Map 객체 역시 다르지 않다.

let obj = { name: "I am Obj" };
let map = new Map()
map.set(obj, "Developer");

obj = null;

for(let obj of map.keys()) {
	console.log(JSON.stringify(obj)); // {"name":"I am Obj"}
}

여전히 객체는 살아있고 가비지 컬렉터로부터 자유롭다. 당연히 사용되는 객체일 것이라 예상이 된다면 제거하지 않는다. 하지만 정말로 사용되는 객체와 그렇지 않는 객체를 가비지 컬렉터가 알아서 찾아 메모리에서 정리해 주기를 바란다면 잘못된 생각이다. 코드에서 더 이상 사용이 안되는 객체가 명확히 파악되어야 하는 게 우선이다. 하지만 이게 어디 쉬운 일인가? 코드는 수많은 객체가 얽혀있다. 게다가 나만 객체를 만들지 않는다. 그러한 객체들의 라이프 사이클을 일일이 관리하는 게 가능하기나 할까? 그냥 가비지 컬렉터에게 맡겨 청소가 안 되는 곳은 어쩔수 없고 확실히 청소가 가능한 곳만 부탁하는 게 오히려 정신 건강에 더 좋을 수도 있겠다. 그나마 능력있는 메모리 청소부가 알아서 해주는 것도 어디인가?

가비지 컬렉터는 예측 가능하거나 완벽한 작동 시스템이 아니다.

자바스크립트를 가동하는 엔진에 따라 가비지 컬렉터의 작동 시기와 방법 및 그 결과는 다를 수 있다. 가비지 컬렉터를 일관된 솔루션으로 맹신하면 안된다.

WeakMap

자, 이제 WeakMap 객체를 들여다 보자. WeakMap 객체를 설명할 때 빠지지 않는 수식어가 있다. 바로 "약한 참조"라는 것이다. 그래서 객체의 이름도 Weak(약한)Map이다. 메모리의 효율을 위해서 등장한 객체라면 앞서 설명한 경우와 어떤점이 다른지 알아보자.

먼저 Map 객체와 다르게 데이터를 가리키는 키(key)를 객체 또는 심벌(symbol)로만 지정이 가능하다. 앞서 작성한 코드를 Map 객체가 아닌 WeakMap 객체로 마이그레이션 해보자.

let obj = { name: "I am Obj" };
let weakMap = new WeakMap()
weakMap.set(obj, "Developer");

obj = null;

// 사실 여기서 코드상으로 확인하기가 어렵다.

안타깝지만 실제로 객체가 완전하게 제거되었는지는 코드로 확인하기가 어렵다. obj가 이제 null 값으로 재정의 되었으니 당연히 obj를 키로 사용해서 객체의 존재 여부를 확힌할 길이 없는 것은 분명해 보인다. 특성상 WeakMap 객체가 제공하는 API는 매우 제한적이다. 실질적인 확인 방법은 실제로 메모리를 들여다 보는 것이다. 크롬 개발자 도구의 'Memory' 탭에서 전후의 메모리를 스냅샵한 후 'WeakMap'으로 필터링하면 확인할 수 있다.

확실히 WeakMap 객체를 사용하게 되면 메모리 효율성이 높아질 것 같다. 그리고 명시적으로 객체의 라이프 사이클이 정해지니 가비지 컬렉터에 의해서 제거될 것인가를 걱정할 필요도 없을 것 같다. 

하위 트리 탐색

  • Constructor

    WeakMap 객체를 생성하는 방법을 나타낸다.

    • WeakMap()

      새로운 WeakMap 객체의 인스턴스를 생성한다.

  • [[Prototype]]

    • Methods

      • delete()

        지정된 키(key)를 갖는 요소를 제거한다.

      • get()

        지정된 키(key)를 갖는 요소의 값을 반환한다.

      • has()

        지정된 키가 존재하는지의 여부를 불리언(boolean)으로 반환한다.

      • set()

        지정된 키(key)와 값(value)으로 요소를 추가한다.

버전 명세

ECMAScript 2024(15th Edition)
#sec-weakmap-objects

지원 웹브라우저