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

JavaScript에서 순환 참조가 메모리 누수를 일으키는 이유와 해결 방법

순환 참조(Circular Reference)란?

두 변수가 서로를 참조하면 순환 참조(circular reference)가 형성되며, 이때 각 객체의 참조 카운트는 1이 됩니다.

순수한 가비지 컬렉션(GC) 기반의 시스템에서는 해당 변수들을 가리키는 외부 참조가 더 이상 없다면 순환 참조 자체는 큰 문제가 되지 않습니다. 이 경우 선언된 변수들은 가비지 컬렉터에 의해 정상적으로 수거됩니다.

반면 참조 카운팅(reference counting) 방식에서는 서로를 참조하는 두 객체의 참조 카운트가 절대 0이 될 수 없기 때문에 어느 한쪽 객체도 메모리에서 제거되지 못합니다.

참조 카운팅과 가비지 컬렉션을 함께 사용하는 하이브리드 방식의 시스템 역시 순환 참조를 제대로 식별하지 못하면 메모리 누수가 발생할 수 있습니다.

예제

다음 예제는 JavaScript 객체와 DOM 객체 사이에 형성된 순환 참조를 보여줍니다. JavaScript 객체가 DOM 객체(Div)를 참조하고, 동시에 DOM 객체(Div)는 "expando" 속성을 통해 JavaScript 객체(obj)를 다시 참조합니다. 두 객체가 서로를 참조하고 있는 상태이므로 어느 하나도 제거될 수 없으며, 결국 메모리 누수로 이어집니다.

<html>
<body>
<script>
    window.onload = function(){
    var obj = document.getElementById("DivElement");
    document.getElementById("DivElement").expandoProperty = obj;
    obj.String = new Array(1000).join(new Array(2000));
    };
</script>
</body>
</html>

메모리 누수 피하기

다음 예제에서는 처음에 JavaScript 객체와 DOM 객체가 순환 참조 상태에 놓여 있습니다. 하지만 마지막에 JavaScript 객체에 null을 할당하면, JavaScript 객체가 더 이상 DOM 객체를 가리키지 않게 되면서 순환 참조의 고리가 끊어집니다. 그 결과 두 객체 모두 참조 카운트가 0이 될 수 있고, 가비지 컬렉터에 의해 정상적으로 메모리에서 해제됩니다.

이처럼 사용이 끝난 객체 참조에는 명시적으로 null을 할당하는 습관이 순환 참조로 인한 메모리 누수를 예방하는 간단하면서도 효과적인 방법입니다.

예제

<html>
<body>
<script>
    window.onload = function(){
    var obj = document.getElementById("DivElement");
    document.getElementById("DivElement").expandoProperty = obj;
    obj.String = new Array(1000).join(new Array(2000));
    obj = null;      // 순환 참조를 끊는 부분
    };
</script>
</body>
</html>