From da78a9e329e272dedb2400b79a3bdeebff387d47 Mon Sep 17 00:00:00 2001 From: Peter Bengtsson Date: Tue, 8 Dec 2020 14:42:17 -0500 Subject: initial commit --- .../ko/web/javascript/memory_management/index.html | 183 +++++++++++++++++++++ 1 file changed, 183 insertions(+) create mode 100644 files/ko/web/javascript/memory_management/index.html (limited to 'files/ko/web/javascript/memory_management') diff --git a/files/ko/web/javascript/memory_management/index.html b/files/ko/web/javascript/memory_management/index.html new file mode 100644 index 0000000000..22cccc5e76 --- /dev/null +++ b/files/ko/web/javascript/memory_management/index.html @@ -0,0 +1,183 @@ +--- +title: 자바스크립트의 메모리관리 +slug: Web/JavaScript/Memory_Management +translation_of: Web/JavaScript/Memory_Management +--- +

{{JsSidebar("Advanced")}}
+ C 언어같은 저수준 언어에서는 메모리 관리를 위해 malloc() 과 free()를 사용한다. 반면, 자바스크립트는 객체가 생성되었을 때 자동으로 메모리를 할당하고 쓸모 없어졌을 때 자동으로 해제한다(가비지 컬렉션). 이러한 자동 메모리 관리는 잠재적 혼란의 원인이기도 한데, 개발자가 메모리 관리에 대해 고민할 필요가 없다는 잘못된 인상을 줄 수 있기 때문이다.

+ +

메모리 생존주기

+ +

메모리 생존주기는 프로그래밍 언어와 관계없이 비슷하다.

+ +
    +
  1. 필요할때 할당한다.
  2. +
  3. 사용한다. (읽기, 쓰기)
  4. +
  5. 필요없어지면 해제한다. 
  6. +
+ +

두 번째 부분은 모든 언어에서 명시적으로 사용된다. 그러나 첫 번째 부분과 마지막 부분은 저수준 언어에서는 명시적이며, 자바스크립트와 같은 대부분의 고수준 언어에서는 암묵적으로 작동한다. 

+ +

자바스크립트에서 메모리 할당

+ +

값 초기화

+ +

프로그래머를 할당 문제로 괴롭히지 않기 위해서, 자바스크립트는 값을 선언할 때 자동으로 메모리를 할당한다.

+ +
+
var n = 123; // 정수를 담기 위한 메모리 할당
+var s = 'azerty'; // 문자열을 담기 위한 메모리 할당
+
+var o = {
+  a: 1,
+  b: null
+}; // 오브젝트와 그 오브젝트에 포함된 값들을 담기 위한 메모리 할당
+
+// (오브젝트처럼) 배열과 배열에 담긴 값들을 위한 메모리 할당
+var a = [1, null, 'abra'];
+
+function f(a){
+  return a + 2;
+} // 함수를 위한 할당(함수는 호출 가능한 오브젝트이다)
+
+// 함수식 또한 오브젝트를 담기위한 메모리를 할당한다.
+someElement.addEventListener('click', function(){
+  someElement.style.backgroundColor = 'blue';
+}, false);
+
+
+ +

함수 호출을 통한 할당

+ +

함수 호출의 결과 메모리 할당이 일어나기도 한다. 

+ +
var d = new Date(); // Date 개체를 위해 메모리를 할당
+
+var e = document.createElement('div'); // DOM 엘리먼트를 위해 메모리를 할당한다.
+
+ +

메소드가 새로운 값이나 오브젝트를 할당하기도 한다.

+ +
var s = 'azerty';
+var s2 = s.substr(0, 3); // s2는 새로운 문자열
+// 자바스크립트에서 문자열은 immutable 값이기 때문에,
+// 메모리를 새로 할당하지 않고 단순히 [0, 3] 이라는 범위만 저장한다.
+
+var a = ['ouais ouais', 'nan nan'];
+var a2 = ['generation', 'nan nan'];
+var a3 = a.concat(a2);
+// a 와 a2 를 이어붙여, 4개의 원소를 가진 새로운 배열
+
+ +

값 사용

+ +

값 사용이란 기본적으로는 할당된 메모리를 읽고 쓰는 것을 의미한다. 변수나 오브젝트 속성 값을 읽고 쓸때 값 사용이 일어난다. 또 함수 호출시 함수에 인수를 넘길때도 일어난다. 

+ +

할당된 메모리가 더 이상 필요없을 때 해제하기

+ +

이 단계에서 대부분의 문제가 발생한다. "할당된 메모리가 더 이상 필요없을 때"를 알아내기가 어렵기 때문이다.

+ +

저수준 언어에서는 메모리가 필요없어질 때를 개발자가 직접 결정하고 해제하는 방식을 사용한다. 

+ +

자바스크립트와 같은 고수준 언어들은 "가비지 콜렉션(GC)"이라는 자동 메모리 관리 방법을 사용한다. 가비지 콜렉터의 목적은 메모리 할당을 추적하고 할당된 메모리 블록이 더 이상 필요하지 않게게 되었는지를 판단하여 회수하는 것이다. 이러한 자동 메모리 관리 프로세스는 궁극의 방법은 아니다. 왜냐하면 어떤 메모리가 여전히 필요한지 아닌지를 판단하는 것은 비결정적 문제이기 때문이다.

+ +

가비지 콜렉션

+ +

위에서 언급한 것처럼 "더 이상 필요하지 않은" 모든 메모리를 찾는건 비결정적 문제다. 따라서 가비지 컬렉터들은 이 문제에 대한 제한적인 해결책을 구현한다. 이 섹션에서는 주요한 가비지 컬렉션 알고리즘들과 그 한계를 이해하는데 필요한 개념을 설명한다.

+ +

참조

+ +

가비지 콜렉션 알고리즘의 핵심 개념은 참조이다. A라는 메모리를 통해 (명시적이든 암시적이든) B라는 메모리에 접근할 수 있다면 "B는 A에 참조된다" 라고 한다. 예를 들어 모든 자바스크립트 오브젝트는 prototype 을 암시적으로 참조하고 그 오브젝트의 속성을 명시적으로 참조한다.

+ +

앞으로 "오브젝트"라는 어휘의 의미를 넓혀서 기존의 자바스크립트 오브젝트뿐만 아니라 함수 스코프도 포괄하자.

+ +

참조-세기(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 = "yo";     // 이제 맨 처음 'y' 변수가 참조했던 오브젝트를 참조하는 오브젝트는 없다.
+              // (역자: 참조하는 유일한 변수였던 y에 다른 값을 대입했다)
+              // 이제 오브젝트에 가비지 콜렉션이 수행될 수 있을까?
+              // 아니다. 오브젝트의 'a' 속성이 여전히 'z' 변수에 의해 참조되므로
+              // 메모리를 해제할 수 없다.
+
+z = null;     // 'z' 변수에 다른 값을 할당했다.
+              // 이제 맨 처음 'x' 변수가 참조했던 오브젝트를 참조하는
+              // 다른 변수는 없으므로 가비지 콜렉션이 수행된다.
+
+ +

한계: 순환 참조

+ +

순환 참조를 다루는 일에는 한계가 있다. 다음 예제에서는 두 객체가 서로 참조하는 속성으로 생성되어 순환 구조를 생성한다. 함수 호출이 완료되면 이 두 객체는 스코프를 벗어나게 될 것이며, 그 시점에서 두 객체는 불필요해지므로 할당된 메모리는 회수되어야 한다. 그러나 두 객체가 서로를 참조하고 있으므로, 참조-세기 알고리즘은 둘 다 가비지 컬렉션의 대상으로 표시하지 않는다. 이러한 순환 참조는 메모리 누수의 흔한 원인이다.

+ +
function f(){
+  var o = {};
+  var o2 = {};
+  o.a = o2; // o는 o2를 참조한다.
+  o2.a = o; // o2는 o를 참조한다.
+
+  return "azerty";
+}
+
+f();
+
+ +

실제 예제

+ +

인터넷 익스플로러 6, 7 은 DOM 오브젝트에 대해 참조-세기 알고리즘으로 가비지 콜렉션을 수행한다. 흔히, 이 두 브라우저에서는 다음과 같은 패턴의 메모리 누수가 발생한다. 

+ +
var div = document.createElement("div");
+div.onclick = function(){
+  doSomething();
+}; // div 오브젝트는 이벤트 핸들러를 'onclick' 속성을 통해 참조한다.
+// 이벤트 핸들러의 스코프에도 div 오브젝트가 있으므로 div 오브젝트에 접근할 수 있다. 따라서 이벤트 핸들러도 div 오브젝트를 참조한다.
+// 순환이 발생했고 메모리 누수가 일어난다.
+
+ +

표시하고-쓸기(Mark-and-sweep) 알고리즘

+ +

이 알고리즘은 "더 이상 필요없는 오브젝트"를 "닿을 수 없는 오브젝트"로 정의한다.

+ +

이 알고리즘은 roots 라는 오브젝트의 집합을 가지고 있다(자바스크립트에서는 전역 변수들을 의미한다). 주기적으로 가비지 콜렉터는 roots로 부터 시작하여 roots가 참조하는 오브젝트들, roots가 참조하는 오브젝트가 참조하는 오브젝트들... 을 닿을 수 있는 오브젝트라고 표시한다. 그리고 닿을 수 있는 오브젝트가 아닌 닿을 수 없는 오브젝트에 대해 가비지 콜렉션을 수행한다.

+ +

이 알고리즘은 위에서 설명한 참조-세기 알고리즘보다 효율적이다. 왜냐하면 "참조되지 않는 오브젝트"는 모두 "닿을 수 없는 오브젝트" 이지만 역은 성립하지 않기 때문이다. 위에서 반례인 순환 참조하는 오브젝트들을 설명했다.

+ +

2012년 기준으로 모든 최신 브라우저들은 가비지 콜렉션에서 표시하고-쓸기 알고리즘을 사용한다. 지난 몇 년간 연구된 자바스크립트 가비지 콜렉션 알고리즘의 개선들은 모두 이 알고리즘에 대한 것이다. 개선된 알고리즘도 여전히 "더 이상 필요없는 오브젝트"를 "닿을 수 없는 오브젝트"로 정의하고 있다.

+ +

순환 참조는 이제 문제가 되지 않는다.

+ +

첫 번째 예제에서 함수가 리턴되고 나서 두 오브젝트는 닿을 수 없다. 따라서 가비지 콜렉션이 일어난다.

+ +

두 번째 예제에서도 마찬가지다. div 변수와 이벤트 핸들러가 roots로 부터 닿을 수 없어지면 순환 참조가 일어났음에도 불구하고 가비지 콜렉션이 일어난다. (역자2: div 선언을 블럭안에다 넣어야 된다.(테스트는 못 해봤다.))

+ +

한계: 수동 메모리 해제.

+ +

어떤 메모리를 언제 해제할지에 대해 수동으로 결정하는 것이 편리할 때가 있습니다. 그리고 수동으로 객체의 메모리를 해제하려면, 객체 메모리에 도달할 수 없도록 명시하는 기능이 있어야 합니다.
+
+ 2019년 현재의 JavaScript에서는 명시적으로 또는 프로그래밍 방식으로 가비지 컬렉션을 작동할 수 없습니다.

+ +

더 보기

+ + -- cgit v1.2.3-54-g00ecf