실무자를 위한 자바스크립트 '가비지 컬렉션' 가이드
개요
자바스크립트는 자동으로 메모리를 관리하는 언어로, 개발자가 직접 free() 같은 함수를 호출하여 메모리를 해제하지 않아도 됨
엔진 내부에 내장된 가비지 컬렉터가 사용하지 않는 객체를 찾아서 메모리에서 해제해주기 때문
하지만 가비지 컬렉터가 항상 모든 문제를 해결해주는 것은 아님
개발자가 의도치 않게 참조를 남겨두거나 불필요한 데이터를 붙잡고 있으면 제거하지 못하고, 이는 '메모리 누수'로 이어짐
자바스크립트 가비지 컬렉션의 기본 개념
자바스크립트 엔진은 메모리를 크게 세 단계로 관리
1) 객체가 생성되면 메모리를 할당
2) 코드에서 이를 사용
3) 더 이상 필요하지 않다고 판단되면 메모리를 해제
1. 참조 카운트 (reference counting)
초기의 카비지 컬렉션 알고리즘은 참조 카운트 방식을 사용
객체가 몇 번 참조되고 있는지를 기록하고, 참조 횟수가 0이되면 메모리에서 제거
하지만, 순환 참조(circular reference)의 경우 서로가 참조하는 구조이기 때문에 메모리 해제가 되지 않게됨
2. 마크 앤 스윕 (mark and sweep)
현재 자바스크립트 엔진(V8)이 사용하는 방식
가비지 컬렉터가 루트 객체를 기준으로 도달 가능한 객체들을 마킹함
루트 객체란 브라우저에서는 windows, Node.js 환경에서는 global과 같은 전역 객체를 의미
루트에서 출발해 참조할 수 있는 모든 객체를 따라가며 표시해두고, 그 외에 표시되지 않은 객체는 "unreachable"로 판단하여 메모리에서 제거
이러면 순환 참조가 있어도 도달할 수 없으면 제거하기 때문에 안전해짐
메모리 누수 사례
1. 클로저로 인한 누수
클로저는 함수가 외부 스코프의 변수를 기억하는 기능
기능 자체는 유용하지만, 필요없는 데이터를 계속 붙잡고 있을 때 문제가 됨
2. 이벤트 리스너 누수
DOM 요소를 제거할 때 이벤트 리스너를 함께 제거하지 않으면 참조가 남게됨
3. DOM 참조 누수
DOM 요소를 전역 변수나 캐시에 저장해두고 해제하지 않는 경우에도 문제가 생김
실무에서의 디버깅 방법
1. 크롬 DevTools의 Memory 탭 활용
Heap snapshot 기능을 통해 현재 메모리 상태를 스냅샷으로 찍어 어떤 객체가 얼마나 차지하고 있는지 확인 가능
Allocation instrumension은 시간 흐름에 따른 메모리 할당과 해제를 추적할 수 있음
Record Allocation Timeline은 객체가 언제 생성되고 해제되는지를 추적해, 특정 함수나 이벤트 핸들러가 원인인지 분석할 수 있도록 도와줌
2. 개발
이벤트 리스너를 등록했다면 반드시 해제
React에서는 useEffect의 cleanup 함수,
Vue에서는 beforeUnmount 훅을 사용하면 자동으로 관리 가능
전역 변수는 최소화해야하며, 필요한 경우 지역 범위 내에서 데이터를 관리하는 구조를 권장
대규모 데이터를 담는 배열이나 Map은 사용이 끝나면 반드시 null 할당이나 clear 메서드를 통해 참조를 끊어야함
WeakMap이나 WeakSet을 활용하면 키가 참조되지 않을 때 자동으로 가비지 컬렉션 대상이 되기 때문에 캐시용도로 적합
Reference
실무자를 위한 자바스크립트 ‘가비지 컬렉션’ 가이드 | 요즘IT
자바스크립트는 자동으로 메모리를 관리하는 언어입니다. 개발자가 직접 free() 같은 함수를 호출해 메모리를 해제할 필요가 없다는 뜻이죠. 대신 엔진 내부에 내장된 가비지 컬렉터(Garbage Collecto
yozm.wishket.com
Reference