Computer >> 컴퓨터 >  >> 프로그래밍 >> JavaScript

JavaScript에서 잊어버린 타이머와 콜백이 메모리 누수를 일으키는 이유

잊어버린 타이머와 콜백(Forgotten Timers/Callbacks)

JavaScript에는 두 가지 타이밍 이벤트가 있습니다. 바로 setTimeout()setInterval()입니다. 전자는 지정된 밀리초만큼 기다린 후 함수를 한 번 실행하고, 후자는 정해진 시간 간격마다 함수를 주기적으로 반복 실행합니다.

객체가 타이머 콜백에 연결되면, 해당 타임아웃이 완료되기 전까지는 객체가 메모리에서 해제되지 않습니다. 특히 setInterval()처럼 스스로를 재설정하며 무한히 반복 실행되는 타이머의 경우, 원본 객체에 대한 외부 참조가 사라졌더라도 가비지 컬렉터(Garbage Collector)가 해당 메모리를 회수하지 못합니다. 이러한 잊어버린 타이머는 JavaScript에서 메모리 누수가 발생하는 가장 흔한 원인 중 하나입니다.

예제 코드

다음 예제에서는 타이머 콜백과 그에 연결된 객체(tiedObject)가 타임아웃이 끝날 때까지 해제되지 않습니다. 그동안 타이머는 스스로를 재설정하며 영원히 실행되기 때문에, 원본 객체에 대한 참조가 더 이상 없더라도 해당 메모리 공간은 절대 수거되지 않습니다.

<html>
<body>
<script>
   for (var i = 0; i < 100000; i++) {
   var tiedObject = {
   callAgain: function() {
      var text = this;
      var value = setTimeout(function() {
      text.callAgain();
      }, 100000);
   }
   }
   tiedObject.callAgain();
   tiedObject = null;
   }
</script>
</body>
</html>

위 코드에서 callAgain 메서드 내부의 클로저가 this에 대한 참조를 유지하고 있고, 이 클로저가 다시 setTimeout에 의해 대기열에 등록되기 때문에 객체가 계속해서 메모리에 남게 됩니다.

메모리 누수 방지 방법

1. setInterval() 또는 setTimeout() 내부에 불필요한 참조를 만들지 않도록 주의하고, 가비지 컬렉션이 이루어질 수 있도록 콜백이 필요한 동안에만 객체를 연결합니다.

2. 더 이상 필요하지 않은 타이머는 직접 제거합니다. 즉, clearTimeout()이나 clearInterval()을 호출하여 타이머를 명시적으로 해제하는 것이 좋습니다.

참고로 IE와 같은 구형 브라우저를 제외하면 Chrome 등 대부분의 최신 브라우저에서는 이러한 문제가 크게 개선되었습니다. 특히 jQuery 같은 라이브러리는 내부적으로 이를 처리하여 메모리 누수가 발생하지 않도록 관리해 줍니다.