가비지 수집 언어에서 누출의 주요 원인은 원치 않는 참조입니다. 메모리 누수를 이해하기 위해 메모리 해제(가비지 컬렉션)가 어떻게 작동하는지 살펴보겠습니다.
Mark-and-sweep 알고리즘 −이 알고리즘은 "객체가 더 이상 필요하지 않음"의 정의를 "객체에 연결할 수 없음"으로 줄입니다. 이 알고리즘은 루트라는 개체 집합에 대한 지식을 가정합니다. JavaScript에서 루트는 전역 객체입니다. 주기적으로 GC는 이러한 루트에서 시작하여 이 루트에서 참조되는 모든 객체를 재귀적으로 찾습니다. 따라서 GC는 루트에서 시작하여 연결할 수 있는 모든 개체를 찾고 연결할 수 없는 모든 개체를 수집합니다.
메모리 누수 유형
1. 전역 변수(선언되지 않음/우발적)
JS에서는 선언 키워드(let, var, const)를 지정하지 않으면 실수로 전역적으로 변수를 선언할 수 있습니다. JS는 전역 스코프에 도달할 때까지 스코프 밖으로 이동하는 것을 찾고, 스코프에서 변수를 찾지 못하면 전역 변수를 생성합니다.
예시
function test() { a = [1, 2, 3] } test() // a was initialized without declaration using a keyword and is now in the global scope. console.log(a)
출력
[1, 2, 3]
이 동작은 변수가 무의식적으로 전역 범위에 존재하고 프로그램이 종료되지 않는 한 해제되지 않기 때문에 메모리 누수를 일으킬 수 있습니다. 이는 선언 키워드를 사용하여 수정할 수 있습니다.
2. 폐쇄
외부 함수에서 변수가 선언되고 중첩된 내부 함수에서 자동으로 사용할 수 있게 되며 중첩된 함수에서 사용/참조되지 않더라도 메모리에 계속 남아 있으면 클로저에서 메모리 누수가 발생합니다.
3. 분리된 DOM/DOM 외부 참조
DOM은 이중 연결 트리이며 트리의 모든 노드를 참조하면 전체 트리가 가비지 수집을 방지할 수 있습니다. Detached DOM 또는 Out of DOM 참조는 DOM에서 제거되었지만 JS를 통해 메모리에 있는 노드를 의미합니다. 변수나 객체에 대한 참조가 어디에나 있는 한 해당 객체는 DOM에서 제거된 후에도 가비지 수집되지 않음을 의미합니다. DOM의 일부를 사용한 후에는 항상 JS에서 참조를 제거하십시오.
4. 이벤트 리스너
addEventListener() 메서드는 이벤트 핸들러를 요소에 연결하고 여러 이벤트 핸들러를 단일 요소에 추가할 수 있습니다. DOM 요소와 이벤트 리스너의 수명 주기가 같지 않으면 메모리 누수가 발생할 수 있습니다.