## 클로저가 메모리 누수를 일으키는 이유 [[클로저 (Closure)|클로저]]는 자신이 선언된 렉시컬 환경의 변수들을 참조하고, 그 참조가 유지되는 한 해당 변수들은 가비지 컬렉션의 대상이 되지 않는다. 그래서 클로저가 불필요하게 오래 유지되거나 많이 생기면 불필요하게 메모리에 남아 있는 변수들이 많아지면서 메모리 누수가 발생할 수 있다. ## 메모리 누수 방지 방법 ### 불필요한 클로저 사용 자제 일회성 작업의 경우에는 클로저를 사용하지 않거나 즉시 해제 가능한 구조로 코드를 작성하는 것이 좋다. ### 명시적 참조 해제 클로저를 더 이상 사용하지 않을 때 클로저 함수 자체, 혹은 클로저에서 참조하고 있는 변수들을 null 등으로 명시적으로 초기화한다. - 내부 변수 참조 해제 ```javascript function outer() { let largeData = new Array(100000).fill("hello"); return function inner() { console.log(largeData[0]); largeData = null; // 불필요해지면 참조 해제 }; } const fn = outer(); fn(); // 사용 후 참조 제거 ``` - 클로저 참조 해제 ```javascript function outer() { let secret = 123; return function inner() { console.log(secret); }; } let closure = outer(); closure(); // 123 closure = null; // 클로저 참조 해제 → GC 대상 ``` > [!Note] 명시적 참조 해제 방법 > - 참조 해제가 목적이라면 `null`이나 `undefined`를 사용하는 것이 일반적 > - 참조는 유지하되 빈 상태로 만들고 싶다면 빈 배열, 빈 객체 등으로 초기화할 수 있다. ### 이벤트 리스너, 타이머 정리 ```javascript // 이벤트 리스너 정리하기 function bindClickHandler() { function handleClick() { console.log("clicked"); } document.addEventListener("click", handleClick); // 이후 필요 없어지면 정리 document.removeEventListener("click", handleClick); } ``` ```javascript // 타이머 정리하기 const id = setInterval(() => { console.log("running"); }, 1000); // 나중에 중단 clearInterval(id); ``` ### 스코프 제한 즉시 실행 함수나 블록 스코프를 이용해서 클로저의 스코프를 제한하는 방법으로 자연스럽게 수명을 종료시키고 가비지 컬렉션의 대상으로 만드는 방법도 있다. <!--todo: 즉시 실행 함수 좋은 예시 고민해보기--> ## 메모리 누수 대처 방법 ### 모니터링 개발자 도구의 Heap Snapshot을 통해 메모리를 점유하고 있는 객체를 추적하고, 클로저나 이벤트 리스너 등 불필요한 참조를 제거한다. ### 구조 개선 불필요하게 사용되는 클로저가 있는지 확인한다. 약한 참조를 사용하는 [WeakMap](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakMap), [WeakSet](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakSet)을 활용해서 참조를 약화하는 것도 방법이 된다.