diff options
Diffstat (limited to 'files/ko/web/javascript/memory_management/index.html')
-rw-r--r-- | files/ko/web/javascript/memory_management/index.html | 183 |
1 files changed, 183 insertions, 0 deletions
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 +--- +<p>{{JsSidebar("Advanced")}}<br> + C 언어같은 저수준 언어에서는 메모리 관리를 위해 <code>malloc()</code> 과 <code>free()</code>를 사용한다. 반면, 자바스크립트는 객체가 생성되었을 때 자동으로 메모리를 할당하고 쓸모 없어졌을 때 자동으로 해제한다(가비지 컬렉션). 이러한 자동 메모리 관리는 잠재적 혼란의 원인이기도 한데, 개발자가 메모리 관리에 대해 고민할 필요가 없다는 잘못된 인상을 줄 수 있기 때문이다.</p> + +<h2 id="메모리_생존주기">메모리 생존주기</h2> + +<p>메모리 생존주기는 프로그래밍 언어와 관계없이 비슷하다.</p> + +<ol> + <li>필요할때 할당한다.</li> + <li>사용한다. (읽기, 쓰기)</li> + <li>필요없어지면 해제한다. </li> +</ol> + +<p>두 번째 부분은 모든 언어에서 명시적으로 사용된다. 그러나 첫 번째 부분과 마지막 부분은 저수준 언어에서는 명시적이며, 자바스크립트와 같은 대부분의 고수준 언어에서는 암묵적으로 작동한다. </p> + +<h3 id="자바스크립트에서_메모리_할당">자바스크립트에서 메모리 할당</h3> + +<h4 id="값_초기화">값 초기화</h4> + +<p>프로그래머를 할당 문제로 괴롭히지 않기 위해서, 자바스크립트는 값을 선언할 때 자동으로 메모리를 할당한다.</p> + +<div style="overflow: hidden;"> +<pre class="brush: js">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); +</pre> +</div> + +<h4 id="함수_호출을_통한_할당">함수 호출을 통한 할당</h4> + +<p>함수 호출의 결과 메모리 할당이 일어나기도 한다. </p> + +<pre class="brush: js">var d = new Date(); // Date 개체를 위해 메모리를 할당 + +var e = document.createElement('div'); // DOM 엘리먼트를 위해 메모리를 할당한다. +</pre> + +<p>메소드가 새로운 값이나 오브젝트를 할당하기도 한다.</p> + +<pre class="brush: js">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개의 원소를 가진 새로운 배열 +</pre> + +<h3 id="값_사용">값 사용</h3> + +<p>값 사용이란 기본적으로는 할당된 메모리를 읽고 쓰는 것을 의미한다. 변수나 오브젝트 속성 값을 읽고 쓸때 값 사용이 일어난다. 또 함수 호출시 함수에 인수를 넘길때도 일어난다. </p> + +<h3 id="할당된_메모리가_더_이상_필요없을_때_해제하기">할당된 메모리가 더 이상 필요없을 때 해제하기</h3> + +<p>이 단계에서 대부분의 문제가 발생한다. "할당된 메모리가 더 이상 필요없을 때"를 알아내기가 어렵기 때문이다.</p> + +<p>저수준 언어에서는 메모리가 필요없어질 때를 개발자가 직접 결정하고 해제하는 방식을 사용한다. </p> + +<p>자바스크립트와 같은 고수준 언어들은 "<a href="https://en.wikipedia.org/wiki/Garbage_collection_(computer_science)">가비지 콜렉션</a>(GC)"이라는 자동 메모리 관리 방법을 사용한다. 가비지 콜렉터의 목적은 메모리 할당을 추적하고 할당된 메모리 블록이 더 이상 필요하지 않게게 되었는지를 판단하여 회수하는 것이다. 이러한 자동 메모리 관리 프로세스는 궁극의 방법은 아니다. 왜냐하면 어떤 메모리가 여전히 필요한지 아닌지를 판단하는 것은 <a class="external" href="http://en.wikipedia.org/wiki/Decidability_%28logic%29">비결정적</a> 문제이기 때문이다.</p> + +<h2 id="가비지_콜렉션">가비지 콜렉션</h2> + +<p>위에서 언급한 것처럼 "더 이상 필요하지 않은" 모든 메모리를 찾는건 비결정적 문제다. 따라서 가비지 컬렉터들은 이 문제에 대한 제한적인 해결책을 구현한다. 이 섹션에서는 주요한 가비지 컬렉션 알고리즘들과 그 한계를 이해하는데 필요한 개념을 설명한다.</p> + +<h3 id="참조">참조</h3> + +<p>가비지 콜렉션 알고리즘의 핵심 개념은 <em>참조</em>이다. A라는 메모리를 통해 (명시적이든 암시적이든) B라는 메모리에 접근할 수 있다면 "B는 A에 참조된다" 라고 한다. 예를 들어 모든 자바스크립트 오브젝트는 <a href="/en/JavaScript/Guide/Inheritance_and_the_prototype_chain" title="en/JavaScript/Guide/Inheritance_and_the_prototype_chain">prototype</a> 을 암시적으로 참조하고 그 오브젝트의 속성을 명시적으로 참조한다.</p> + +<p>앞으로 "오브젝트"라는 어휘의 의미를 넓혀서 기존의 자바스크립트 오브젝트뿐만 아니라 함수 스코프도 포괄하자.</p> + +<h3 id="참조-세기Reference-counting_가비지_콜렉션">참조-세기(Reference-counting) 가비지 콜렉션</h3> + +<p>참조-세기 알고리즘은 가장 소박한 알고리즘이다. 이 알고리즘은 "더 이상 필요없는 오브젝트"를 "어떤 다른 오브젝트도 참조하지 않는 오브젝트"라고 정의한다. 이 오브젝트를 "가비지"라 부르며, 이를 참조하는 다른 오브젝트가 하나도 없는 경우, 수집이 가능하다.</p> + +<h4 id="예제">예제</h4> + +<pre class="brush: js">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' 변수가 참조했던 오브젝트를 참조하는 + // 다른 변수는 없으므로 가비지 콜렉션이 수행된다. +</pre> + +<h4 id="한계_순환_참조">한계: 순환 참조</h4> + +<p>순환 참조를 다루는 일에는 한계가 있다. 다음 예제에서는 두 객체가 서로 참조하는 속성으로 생성되어 순환 구조를 생성한다. 함수 호출이 완료되면 이 두 객체는 스코프를 벗어나게 될 것이며, 그 시점에서 두 객체는 불필요해지므로 할당된 메모리는 회수되어야 한다. 그러나 두 객체가 서로를 참조하고 있으므로, 참조-세기 알고리즘은 둘 다 가비지 컬렉션의 대상으로 표시하지 않는다. 이러한 순환 참조는 메모리 누수의 흔한 원인이다.</p> + +<pre class="brush: js">function f(){ + var o = {}; + var o2 = {}; + o.a = o2; // o는 o2를 참조한다. + o2.a = o; // o2는 o를 참조한다. + + return "azerty"; +} + +f(); +</pre> + +<h4 id="실제_예제">실제 예제</h4> + +<p>인터넷 익스플로러 6, 7 은 DOM 오브젝트에 대해 참조-세기 알고리즘으로 가비지 콜렉션을 수행한다. 흔히, 이 두 브라우저에서는 다음과 같은 패턴의 메모리 누수가 발생한다. </p> + +<pre class="brush: js">var div = document.createElement("div"); +div.onclick = function(){ + doSomething(); +}; // div 오브젝트는 이벤트 핸들러를 'onclick' 속성을 통해 참조한다. +// 이벤트 핸들러의 스코프에도 div 오브젝트가 있으므로 div 오브젝트에 접근할 수 있다. 따라서 이벤트 핸들러도 div 오브젝트를 참조한다. +// 순환이 발생했고 메모리 누수가 일어난다. +</pre> + +<h3 id="표시하고-쓸기Mark-and-sweep_알고리즘">표시하고-쓸기(Mark-and-sweep) 알고리즘</h3> + +<p>이 알고리즘은 "더 이상 필요없는 오브젝트"를 "닿을 수 없는 오브젝트"로 정의한다.</p> + +<p>이 알고리즘은 <em>roots</em> 라는 오브젝트의 집합을 가지고 있다(자바스크립트에서는 전역 변수들을 의미한다). 주기적으로 가비지 콜렉터는 roots로 부터 시작하여 roots가 참조하는 오브젝트들, roots가 참조하는 오브젝트가 참조하는 오브젝트들... 을 <em>닿을 수 있는 오브젝트</em>라고 표시한다. 그리고 닿을 수 있는 오브젝트가 아닌 닿을 수 없는 오브젝트에 대해 가비지 콜렉션을 수행한다.</p> + +<p>이 알고리즘은 위에서 설명한 참조-세기 알고리즘보다 효율적이다. 왜냐하면 "참조되지 않는 오브젝트"는 모두 "닿을 수 없는 오브젝트" 이지만 역은 성립하지 않기 때문이다. 위에서 반례인 순환 참조하는 오브젝트들을 설명했다.</p> + +<p>2012년 기준으로 모든 최신 브라우저들은 가비지 콜렉션에서 표시하고-쓸기 알고리즘을 사용한다. 지난 몇 년간 연구된 자바스크립트 가비지 콜렉션 알고리즘의 개선들은 모두 이 알고리즘에 대한 것이다. 개선된 알고리즘도 여전히 "더 이상 필요없는 오브젝트"를 "닿을 수 없는 오브젝트"로 정의하고 있다.</p> + +<h4 id="순환_참조는_이제_문제가_되지_않는다.">순환 참조는 이제 문제가 되지 않는다.</h4> + +<p>첫 번째 예제에서 함수가 리턴되고 나서 두 오브젝트는 닿을 수 없다. 따라서 가비지 콜렉션이 일어난다.</p> + +<p>두 번째 예제에서도 마찬가지다. div 변수와 이벤트 핸들러가 roots로 부터 닿을 수 없어지면 순환 참조가 일어났음에도 불구하고 가비지 콜렉션이 일어난다. (역자2: div 선언을 블럭안에다 넣어야 된다.(테스트는 못 해봤다.))</p> + +<h4 id="한계_수동_메모리_해제.">한계: 수동 메모리 해제.</h4> + +<p>어떤 메모리를 언제 해제할지에 대해 수동으로 결정하는 것이 편리할 때가 있습니다. 그리고 수동으로 객체의 메모리를 해제하려면, 객체 메모리에 도달할 수 없도록 명시하는 기능이 있어야 합니다.<br> + <br> + 2019년 현재의 JavaScript에서는 명시적으로 또는 프로그래밍 방식으로 가비지 컬렉션을 작동할 수 없습니다.</p> + +<p>더 보기</p> + +<ul> + <li><a class="external" href="http://www.ibm.com/developerworks/web/library/wa-memleak/">IBM article on "Memory leak patterns in JavaScript" (2007)</a></li> + <li><a class="external" href="http://msdn.microsoft.com/en-us/magazine/ff728624.aspx">Kangax article on how to register event handler and avoid memory leaks (2010)</a></li> +</ul> |