Contents

Javascript Weakref

js 스터디를 진행하면서, 자바스크립트는 명시적으로 객체를 메모리에서 해제하는 방법이 없는 언어라는 이야기가 나왔다.

그럼 객체가 계속해서 메모리에 남아있는 현상을 방지하기 위해서는 어떻게 해야 하냐.. 에 대한 이야기가 오고 갔었는데, 그 중 이야기가 나왔던 WeakRef에 대해 알아봤다.

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

WeakRef란

WeakRef 객체는 다른 객체에 대한 ‘약한 참조’를 가지고 있도록 한다. 그래서 그 객체가 가비지 컬렉팅되는 것을 언어 수준에서 더 이상 막지 않는다.

객체에 대한 약한 참조는 가비지 컬렉터의 대상이 되는 것을 맞기 않는 레퍼런스이다. 반대로 일반 (또는 강한) 참조는 객체를 계속해서 메모리에 남게 한다.

객체에 대한 강한 참조가 더 이상 남지 않을 때, 자바스크립트의 가비지 컬렉터는 객체에 대한 메모리를 회수해간다. 그리고 GC가 일어나면 약한 참조로부터의 객체를 더 이상 얻을 수 없다.

웬만하면 쓰지 마라

이 말이 두 번째로 올 지는 몰랐다!

WeakRef의 올바르게 사용하려면 조심해야 한다. 그리고 웬만하면 쓰지 않는 것이 좋다고 한다.

스펙에 의해 보장되지 않은 어느 특정 행동을 하는 것이 좋지 않기 때문이다.

언제, 어떻게 가비지 컬렉션이 일어날 지는 자바스크립트 엔진 구현체에 온전히 맡겨야 하는 일이다.

한 엔진에서만 동작하는 것을 보장하는 행동은 다른 엔진에서, 동일 엔진의 다른 버전에서, 또는 동일 버전의 동일 엔진의 다른 상황에서 다르게 동작할 수도 있기 때문이다.

그러려고 스펙을 만들어 놓은 것이다. 다시 한번 느끼지만 자바스크립트는 실제 프로그래밍 언어라기보단 스펙에 가깝다는 느낌이다. 모든 엔진들이 이 스펙을 바탕으로 동작하니 자바스크립트를 공부한다는 것은 표준적인 상황에서 동작하는 자바스크립트의 스펙을 공부한다는 의미인 것이다.

그리고 가비지 컬렉터는 이 스펙에 비해 매우 복잡하게 동작한다.

어플리케이션이나 라이브러리가 GC가 WeakRef를 청소하거나 cleanup callback을 언제 동작하는지 예측해서 무언가 행하려 한다면, 매우 높은 확률로 실패할 것이다.

  • 어떤 객체는 동시에 unreachable 해지는 다른 객체에 비해 더 빨리 가비지 컬렉팅될 수 있다. (generational collection)
  • 가비지 컬렉션 작업은 incremental하고 동시적인 기법을 사용해서 시분할이 되어 일어날 수 있다.
  • 다양한 런타임 메트릭이 메모리 사용량과 응답성을 결정한다.
  • 자바스크립트 엔진 자체에서 unreachable 해보이는 객체들에 대한 레퍼런스를 들고 있을 수도 있다. (클로저, 인라인 캐시 등)
  • 서로 다른 자바스크립트 엔진들은 가비지 컬렉션을 다르게 수행할 수 있고, 같은 엔진에서도 버전에 따라 알고리즘이 상이할 수 있다.
  • 특정 API와 함께 사용하는 등 복잡한 요인으로 인해 객체가 예측 불가능한 시간 동안 가비지 컬렉팅되지 않고 유지될 수도 있다.

그럼 언제 쓸까?

약한 참조의 주된 사용 목적은 캐시나 큰 객체를 들고 있는 매핑을 구현하는 것이다.

  • 큰 객체가 전부 메모리 상에 살아 있을 필요가 없다면 객체의 일부분은 가비지 컬렉팅이 되어도 상관 없다.

Finalization은 객체가 프로그램 실행에 의해 unreachable 해졌을 때 clean up 하기 위한 코드의 실행을 말한다. 사용자 정의 finalizer들을 사용한다면 메모리 릭을 방지하고 가비지 컬렉터가 모르는 리소스들에 대한 관리를 커스텀하게 할 수 있다. (물론 주의를 기울여야 하지만..)