자바스크립트는 동적 언어이기 때문에 변수의 할당과 해제가 자동으로 이루어진다. 자바스크립트는 가비지 컬렉터(Garbage Collector)가 자동으로 메모리를 관리하지만, 기본적인 개념을 이해하고 최적화하면 더 나은 성능을 유지할 수 있다.
1. 자바스크립트 메모리 관리의 기본 개념
자바스크립트의 메모리 관리는 크게 아래에 과정으로 나눌 수 있다:
- 메모리 할당: 프로그램이 데이터를 생성하거나 객체를 생성할 때 메모리를 할당받는 과정이다.
- 메모리 사용: 메모리 사용은 변수나 객체를 참조할 때 발생한다.
- 메모리 회수: 더 이상 필요하지 않은 메모리를 자동으로 회수하는 과정이다. 이를 가비지 컬렉션(Garbage Collection)이라고 한다.
프로그래머가 직접 메모리 해제를 명시적으로 처리하는 언어(C, C++ 등)와 달리, 자바스크립트는 자동으로 메모리 관리를 수행한다. 하지만 그 기저에는 가비지 컬렉션 알고리즘이 존재하며, 가장 많이 사용되는 방식이 바로 Mark-and-Sweep 방식이다.
2. Mark and Sweep 방식의 작동 원리
자바스크립트 엔진이 사용하는 기본적인 가비지 컬렉션 방법은 Mark-and-Sweep이다. 이를 통해 프로그램이 더 이상 필요로 하지 않는 메모리를 안전하게 회수할 수 있다.
Mark-and-Sweep 알고리즘의 작동 과정은 크게 두 가지 단계로 나눌 수 있다:
1) Mark 단계
- 참조 가능한 객체 찾기: 먼저, 자바스크립트 엔진은 프로그램 실행 중 접근할 수 있는 객체들을 탐색하기 시작한다. 이때, 루트 객체(root object)를 기준으로 참조되고 있는 모든 객체를 추적한다. 일반적으로 전역 객체, 함수 호출 스택의 변수, 클로저 등이 루트 객체가 될 수 있다.
- 참조된 객체 표시: 이렇게 참조된 객체들은 "살아있는 객체"로 표시(mark)되며, 이 객체들은 여전히 프로그램에서 사용되고 있다고 간주된다.
2) Sweep 단계
- 필요 없는 객체 제거: Mark 단계에서 참조되지 않은 객체들은 더 이상 프로그램에서 사용되지 않는다고 판단된다. 따라서 이러한 객체들은 "가비지"로 간주되어 메모리에서 제거된다.
- 메모리 회수: 제거된 객체들이 사용하던 메모리는 해제되어 다른 용도로 사용할 수 있도록 반환된다.
이 과정을 통해 프로그램이 더 이상 사용하지 않는 메모리가 자동으로 관리되며, 이는 프로그램의 메모리 누수를 방지하는 중요한 역할을 한다.
3. Mark and Sweep 알고리즘의 예시
function example() {
let obj1 = { name: 'Eric' };
let obj2 = { age: 30 };
obj1.friend = obj2;
obj2.parent = obj1;
return 'done';
}
위 코드에서 obj1과 obj2는 서로를 참조하는 객체이다. 이 객체들은 함수 실행이 끝난 후 더 이상 사용할 수 없지만, 여전히 상호 참조 관계가 있다. Mark 단계에서는 obj1과 obj2 모두 참조되고 있기 때문에 "살아있는 객체"로 표시된다.
그러나 example() 함수가 종료되면 이 객체들에 더 이상 접근할 수 없다. 다음 가비지 컬렉션이 실행될 때, obj1과 obj2는 루트 객체에 의해 참조되지 않으므로, Sweep 단계에서 메모리에서 해제된다.
4. 순환 참조와 Mark and Sweep
앞서 설명한 예시에서 obj1과 obj2는 서로를 참조하고 있다. 이러한 경우를 **순환 참조(circular reference)**라고 한다. 하지만, Mark-and-Sweep 방식에서는 순환 참조가 문제가 되지 않는다.
일부 다른 언어에서는 순환 참조로 인해 메모리가 해제되지 않는 문제가 발생할 수 있다. 그러나 자바스크립트의 Mark-and-Sweep 방식은 루트 객체를 기준으로 참조 여부를 확인하기 때문에, 순환 참조 여부와 상관없이 더 이상 사용되지 않는 객체는 안전하게 메모리에서 해제된다.
5. 가비지 컬렉션의 한계와 최적화
Mark-and-Sweep 방식은 자동 메모리 관리를 제공하지만, 가비지 컬렉션이 항상 즉시 수행되지는 않는다. 자바스크립트 엔진은 주기적으로 가비지 컬렉션을 실행하는데, 그 주기는 프로그램의 상태에 따라 다를 수 있다.
따라서 다음과 같은 최적화를 고려할 필요가 있다:
- 메모리 누수 방지: 불필요한 전역 변수나 클로저를 방치하면 루트 객체에 의해 참조된 상태로 남아 가비지 컬렉션이 발생하지 않을 수 있다. 이런 경우, 명시적으로 변수를 null로 설정하거나 클로저의 사용을 줄여 메모리 누수를 방지할 수 있다.
- 객체 크기 관리: 불필요하게 큰 객체나 배열을 사용하면 메모리 사용량이 크게 증가할 수 있다. 특히 자주 사용되지 않는 데이터를 적절히 정리하여 메모리 효율을 높이는 것이 중요하다.
6. 메모리 관리 팁
- 전역 변수 남용 피하기: 전역 변수는 최대한 피하고, let, const를 사용해 블록 범위 변수를 활용합니다.
- 클로저 주의: 클로저를 사용할 때 불필요한 참조를 남기지 않도록 주의합니다.
- 이벤트 리스너 관리: DOM 요소를 제거할 때는 이벤트 리스너도 함께 제거합니다.
- 대규모 객체 해제: 대규모 데이터를 다루는 객체나 배열은 더 이상 필요하지 않을 때 null로 설정해 명시적으로 메모리를 해제하도록 도와줄 수 있습니다.
결론
자바스크립트의 메모리 관리와 가비지 컬렉션은 개발자가 메모리를 직접 다루지 않도록 해주지만, 그럼에도 불구하고 메모리 사용을 최적화하고 메모리 누수를 방지하기 위한 최적화는 필요하다. Mark-and-Sweep 방식은 이러한 자동 메모리 관리의 핵심 알고리즘으로, 자바스크립트 엔진이 효율적으로 메모리를 관리할 수 있도록 돕는다.
'JavaScript' 카테고리의 다른 글
JavaScript - 코어 자바스트립트 정리 (4~7장, 콜백함수,클로저,프로토타입,클래스) (0) | 2024.09.22 |
---|---|
JavaScript - 코어 자바스트립트 정리 (1~3장, 데이터 타입& 실행 컨텍스트& this) (3) | 2024.09.19 |
JavaScript - 자바스크립트의 싱글 쓰레드와 이벤트 루프 (1) | 2024.09.17 |
JavaScript - this (0) | 2024.09.02 |
JavaScript(JS) 자바스크립트 동작원리 (0) | 2024.05.11 |