[소소한 개발 일지] WeakRef의 약한 참조와 메모리 관리

Explanation

오늘은 WeakRef의 약한 참조와 이를 활용한 메모리 관리에 대해서 알아봅니다!

1. WeakRef

WeakRef는 ES2021에서 FinalizationRegistry와 함께 추가되었는데요. 간단하게 WeakRef는 객체를 약한 참조로 만들고 FinalizationRegistry는 객체가 GC에 의해서 제거될 때의 콜백을 설정할 수 있습니다.

WeakRef는 객체를 약한 참조로 만들고 객체의 가비지 컬렉션을 방지하지 않는데요.
“javascript.info”의 예시를 들어보면,
(https://javascript.info/weakref-finalizationregistry)

‘user’라는 변수에 강한 참조로 변수를 보유하고 이를 ‘admin’이라는 변수에 강한 참조로 복사하면 ‘user’라는 변수를 덮어써도 여전히 admin을 통해서 객체에 접근할 수 있습니다.

하지만 위와 같이 ‘admin’에 WeakRef를 사용해서 객체를 얕은 참조로 복사하면 ‘user’라는 값이 덮어써졌을 때 ‘admin’도 GC 될 수 있도록 할 수 있습니다.

그리고 위와 같이 얕은 참조한 값은 ‘deref()’ 메서드를 통해 가져올 수 있으며, 조건문을 통해 GC 여부를 확인 할 수 있습니다. (가비지 컬렉터에 수집되면 ‘undefined’ 응답)

하지만 브라우저의 가비지 컬렉션은 브라우저가 필요하다 판단할 때 동작하기 때문에 위와 같이 ‘user = null’로 덮어썼다고 해서 바로 GC 되어 ‘admin.deref()’값이 ‘undefined’가 되지는 않습니다. 다만, 얕은 참조로 GC가 언제든지 해당 객체를 제거할 수 있도록 보장합니다.

2. 예시

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakRef

위 코드는 MDN에 나와 있는 예시코드 인데요. 위 코드를 실행해보면, id가 counter인 엘리먼트의 값이 1부터 5까지 증가한 후 사라지고, 일정시간 후에 약한 참조가 해제되며 “The element is gone.” 로그가 찍히는 것을 확인할 수 있습니다.

그래서 정말로 GC가 동작하는지 앞서 이야기한 ‘FinalizationRegistry’를 통해서 확인해 봤는데요.

하지만 위 코드를 실행하면 약한 참조가 해제되며 “The element is gone.” 로그는 찍히지만 FinalizationRegistry의 콜백은 호출되지 않는 걸 확인할 수 있었는데요.

그 말인즉, 정확하게는 브라우저의 WeakRef의 상태 확인과 GC의 동작은 별개로 동작한다는 것입니다. 위 예시에서는 브라우저가 DOM 요소가 삭제되고 이후 브라우저가 더 이상 해당 DOM 요소를 참조하지 않는다고 판단하면 this.ref.deref()는 ‘undefined’를 반환합니다. 그리고 GC가 실행될 때 비로소 FinalizationRegistry 콜백이 실행됩니다.

3. 주의해야 할 점

MDN에 작성되어 있는 주의해야 할 점을 간단히 살펴보면,

– WeakRef를 사용하면 GC를 직접 제어할 수 있는 것은 아닙니다.
– GC는 예측할 수 없으며, 엔진에 따라 다르게 동작할 수 있습니다.
– WeakRef를 사용한 객체는 GC될 가능성이 높지만, 즉시 GC되지는 않습니다.
– GC가 객체를 제거하지 않으면 WeakRef가 계속 객체를 참조할 수도 있습니다.

4. 사용 예시

대표적인 WeakRef 사용처는 캐시 시스템인데요. 일반적으로 데이터를 캐시 하여 사용한다면 강한 참조를 유지하기 때문에 메모리 사용량이 많아질 수 있는데요. 자주 사용되는 데이터가 아니라면, 약한 참조로 사용해서 메모리가 부족하면 자동으로 사라지도록 해서 메모리를 절약할 수 있습니다.

마찬가지로 “javascript.info”의 예시를 보면,

그 밖에도 DOM 요소의 이벤트 리스너 같은 경우에도 DOM이 제거될 때 직접 수동으로 이벤트 리스너를 제거하기 어려운 상황이라면 WeakRef를 사용해서 참조를 약하게 유지하면서 자동으로 정리할 수 있습니다.