자바스크립트의 메모리 관리
C 언어같은 저수준 언어에서는 메모리 관리를 위해 malloc() 과 free()를 사용합니다. 반면, 자바스크립트는 객체가 생성되었을 때 자동으로 메모리를 할당하고 더 이상 필요하지 않을 때 자동으로 해제합니다(가비지 컬렉션).
메모리 생존주기
메모리 생존주기는 프로그래밍 언어와 관계없이 비슷합니다.
- 필요할 때 할당합니다.
- 할당된 메모리를 사용합니다. (읽기, 쓰기)
- 더 이상 필요하지 않으면 해제합니다.
두 번째 부분은 모든 언어에서 명시적으로 사용됩니다. 그러나 첫 번째 부분과 마지막 부분은 저수준 언어에서는 명시적이며, 자바스크립트와 같은 대부분의 고수준 언어에서는 암묵적으로 작동합니다.
가비지 컬렉션
가비지 컬렉션은 프로그램에서 더 이상 사용하지 않는 메모리를 자동으로 정리하는 것입니다.
대표적인 가비지 컬렉션의 방법으로는 레퍼런스 카운팅과 트레이싱이 있습니다.
레퍼런스 카운팅
레퍼런스 카운팅은 한 객체를 참조하는 변수의 수를 추적하는 방식입니다. 객체를 참조하고 있던 변수의 값이 바뀌거나, 변수 스코프를 벗어나면 레퍼런스 카운트가 줄어드는데, 이때 레퍼런스 카운트가 0이 되면, 그 객체와 관련한 메모리는 비울 수 있습니다.
이러한 가비지 컬렉션 기능을 가진 언어(혹은 엔진)는 자바, C#, 자바스크립트 등이 있습니다.
참조-세기(Reference-counting) 가비지 콜렉션
참조-세기 알고리즘은 가장 소박한 알고리즘입니다. 이 알고리즘은 "더 이상 필요없는 오브젝트"를 "어떤 다른 오브젝트도 참조하지 않는 오브젝트"라고 정의합니다. 이 오브젝트를 "가비지"라 부르며, 이를 참조하는 다른 오브젝트가 하나도 없는 경우, 수집이 가능합니다.
예제
var x = {
a: {
b: 2
}
};
// 2개의 오브젝트가 생성되었습니다. 하나의 오브젝트는 다른 오브젝트의 속성으로 참조됩니다.
// 나머지 하나는 'x' 변수에 할당되었습니다.
// 명백하게 가비지 콜렉션 수행될 메모리는 하나도 없습니다.
var y = x; // 'y' 변수는 위의 오브젝트를 참조하는 두 번째 변수입니다.
x = 1; // 이제 'y' 변수가 위의 오브젝트를 참조하는 유일한 변수가 되었습니다.
var z = y.a; // 위의 오브젝트의 'a' 속성을 참조했습니다.
// 이제 'y.a'는 두 개의 참조를 가집니다.
// 'y'가 속성으로 참조하고 'z'라는 변수가 참조합니다.
y = "mozilla"; // 이제 맨 처음 'y' 변수가 참조했던 오브젝트를 참조하는 오브젝트는 없습니다.
// (역자: 참조하는 유일한 변수였던 y에 다른 값을 대입했습니다)
// 이제 오브젝트에 가비지 콜렉션이 수행될 수 있을까요?
// 아닙니다. 오브젝트의 'a' 속성이 여전히 'z' 변수에 의해 참조되므로
// 메모리를 해제할 수 없습니다.
z = null; // 'z' 변수에 다른 값을 할당했습니다.
// 이제 맨 처음 'x' 변수가 참조했던 오브젝트를 참조하는
// 다른 변수는 없으므로 가비지 콜렉션이 수행됩니다.
Copy to mozilla단점: 순환 참조
순환 참조를 다루는 일에는 한계가 있습니다. 다음 예제에서는 두 객체가 서로 참조하는 속성으로 생성되어 순환 구조를 생성합니다. 함수 호출이 완료되면 이 두 객체는 스코프를 벗어나게 될 것이며, 그 시점에서 두 객체는 불필요해지므로 할당된 메모리는 회수되어야 합니다. 그러나 두 객체가 서로를 참조하고 있으므로, 참조-세기 알고리즘은 둘 다 가비지 컬렉션의 대상으로 표시하지 않습니다. 이러한 순환 참조는 메모리 누수의 흔한 원인입니다.
트레이싱
포인터추적관리중 트레이싱의 경우 한 객체에 flag를 두고, 가비지 컬렉션 사이클마다 flag에 표시 후 삭제하는 mark and sweep 방식입니다.
'mark-and-sweep’
이라 불리는 가비지 컬렉션 기본 알고리즘
'가비지 컬렉션’은 대개 다음 단계를 거쳐 수행됩니다.
- 가비지 컬렉터는 루트(root) 정보를 수집하고 이를 ‘mark(기억)’ 합니다.
- 루트가 참조하고 있는 모든 객체를 방문하고 이것들을 ‘mark’ 합니다.
- mark 된 모든 객체에 방문하고 그 객체들이 참조하는 객체도 mark 합니다. 한번 방문한 객체는 전부 mark 하기 때문에 같은 객체를 다시 방문하는 일은 없습니다.
- 루트에서 도달 가능한 모든 객체를 방문할 때까지 위 과정을 반복합니다.
- mark 되지 않은 모든 객체를 메모리에서 삭제합니다.
다음과 같은 객체 구조가 있다고 해봅시다.
‘mark-and-sweep’ 알고리즘 처리 방식
- 첫 번째 단계에선 루트를 mark
- 이후 루트가 참조하고 있는 것들을 mark (한번 방문한 객체는 전부 mark 하기 때문에 같은 객체를 다시 방문하는 일은 없음)
- 도달 가능한 모든 객체를 방문할 때까지, mark 한 객체가 참조하는 객체를 계속해서 mark
- 방문할 수 없었던 객체를 메모리에서 삭제
장점 : 순환 참조는 이제 문제가 되지 않습니다.
첫 번째 예제에서 함수가 반환되고 나서 두 오브젝트는 닿을 수 없습니다. 따라서 가비지 콜렉션이 일어납니다.
두 번째 예제에서도 마찬가지입니다. div 변수와 이벤트 핸들러가 roots로 부터 닿을 수 없으면 순환 참조가 일어났음에도 불구하고 가비지 콜렉션이 일어납니다. (역자2: div 선언을 블럭안에다 넣어야 됩니다.(테스트는 못 해봤습니다.))
단점 : 수동 메모리 해제.
어떤 메모리를 언제 해제할지에 대해 수동으로 결정하는 것이 편리할 때가 있습니다. 그리고 수동으로 객체의 메모리를 해제하려면, 객체 메모리에 도달할 수 없도록 명시하는 기능이 있어야 합니다.
reference
https://ko.javascript.info/garbage-collection
'언어(JS,TS) > 그룹스터디' 카테고리의 다른 글
그룹스터디 [보안 : CORS란?] (0) | 2022.05.05 |
---|---|
그룹스터디 [보안 : 인증방식(Session, Cookie, Token)] (0) | 2022.05.05 |
그룹스터디 [Browser : CSR(Client Side Rendering) vs SSR(Server Side Rendering)] (0) | 2022.05.04 |
그룹스터디 [CS : 캐시(cache)란? 캐시의 일반적인 작동원리] (0) | 2022.05.04 |
그룹스터디 [HTTP : REST API란?] (0) | 2022.05.03 |