대부분의 언어에서 메모리 라이프 사이클은 메모리 할당 → 메모리 사용 → 메모리 해제
의 단계를 거친다.
C같은 low-level 언어의 경우 이 라이프 사이클을 개발자가 malloc()이나 free()를 사용하여 직접 관리를 해주어야 하지만 자바스크립트와 같은 high-level 언어는 대부분 Garbage Collection이라는 자동 메모리 관리
를 사용하기 때문에 개발자가 별도의 신경을 쓰지 않는다.
메모리 누수란, 더이상 어플리케이션에서 사용하지 않는도 불구하고 메모리 해제가 되지 않는 상태를 말한다.
📌 Case(1)
// people 변수는 해당 object를 참조한다 .
let people = {
name: "Jane"
};
//null값을 재할당한다.
people = null;
{name: "Jane"}
이라는 값은 더이상 참조되지 않기 때문에 GC에 의해 메모리가 해제된다.
📌 Case(2)
// people 변수는 해당 object를 참조한다 .
let people = {
name: "Jane"
};
//girl에 people의 참조중인 값의 주소를 할당한다.
girl = people;
//people에 null을 할당한다.
people = null;
people은 더이상 {name: "Jane"}
을 참조하고 있지 않지만, girl은 참조하고 있기 때문에 해당 object는 여전히 메모리에 남아있게 된다.
📌 Case(3)
// people 변수는 해당 object를 참조한다 .
let people = {
name: "Jane"
};
//girl에 people의 참조중인 값의 주소를 할당한다.
girl = people;
//people에 null을 할당한다.
people = null;
//girl에 null을 할당한다.
girl = null;
만약 위의 코드처럼 people, girl에 null을 할당하고 나면 {name: "Jane"}
를 참조하고 있는 곳이 없기 때문에 해당 값은 GC에 의해 처리된다.
📣 하지만 reference-counting 방식은 순환참조(Circular reference)가 이루어지는 경우 메무리 누수의 요인이 된다는 문제점이 있다. 아래 예시 코드를 보자.
function couple() {
const jane = {};
const sam = {};
// jane.bf는 sam을 참조한다
jane.bf = sam;
// sam.gf는 jane을 참조한다
sam.gf = jane;
return 'circular';
}
couple();
couple()함수가 호출되고 끝나서 더이상 필요한 값이 아닌데도 불구하고 서로에 대한 참조가 걸려있기 때문에 GC는 이 값들에 대한 메모리를 해제하지 않아 계속해서 메모리에 남아 있는다. 이러한 순환참조는 메모리 누수의 주된 요인이라고 할 수 있다.
위의 reference-counting의 문제점으로는 불필요한 값임에도 불구하고 어디에선가
참조가 걸려있다면 이에 대한 메모리를 해제하지 않는다는 문제점이 있었다. 반면에 Mark-and-sweep 알고리즘은 이 값이 참조되고 있는 값인지에 중점을 두지않고 도달가능성(reachablility)
에 중점을 둔다.
따라서 어떤 값에 대한 참조가 없는 경우는 당연히 도달이 불가능하기 때문에 메모리가 해제되어야 하는 값으로 여겨지고, 참조 되고 있다고 하더라도 root로부터 도달할수 없다고 여겨진다면 처리된다.
2012년부터 모던 브라우저들은 mark-and-sweep 방식의 GC를 사용하고 있다. reference-counting 방식에서 문제가 되었던 아래의 예시를 다시 살펴보자.
function Couple() {
const jane = {};
const sam = {};
// jane.bf는 sam을 참조한다
jane.bf = sam;
// sam.gf는 jane을 참조한다
sam.gf = jane;
return 'circular';
}
Couple();
예시에서 Couple()
이라는 함수 가 호출된 후 'circular'값이 return 되고 끝난 후에는 더이상 root에서 jane과 same에 도달할 수 없기 때문에 해당 값들은 GC에 의해서 메모리가 해제된다.