>  기사  >  웹 프론트엔드  >  me_javascript 기술을 통해 JavaScript의 가비지 수집 메커니즘과 메모리 관리를 알아보세요.

me_javascript 기술을 통해 JavaScript의 가비지 수집 메커니즘과 메모리 관리를 알아보세요.

WBOY
WBOY원래의
2016-05-16 15:30:331218검색

1. 가비지 수집 메커니즘 - GC

Javascript에는 자동 가비지 수집 메커니즘(GC: Garbage Collection)이 있는데, 이는 실행 환경이 코드 실행 중에 사용되는 메모리를 관리한다는 의미입니다.

원리: 가비지 수집기는 주기적으로(주기적으로) 더 이상 사용되지 않는 변수를 찾아 메모리를 해제합니다.

JavaScript 가비지 컬렉션의 메커니즘은 매우 간단합니다. 더 이상 사용되지 않는 변수를 찾은 다음 해당 변수가 차지하는 메모리를 해제합니다. 그러나 이 프로세스는 오버헤드가 상대적으로 크기 때문에 실시간이 아니므로 가비지 컬렉터가 수행합니다. 정해진 일정을 따릅니다 특정 시간 간격으로 주기적으로 실행합니다.

더 이상 사용되지 않는 변수는 수명이 끝난 변수입니다. 물론 로컬 변수만 가능합니다. 전역 변수의 수명은 브라우저가 페이지를 언로드할 때까지 끝나지 않습니다. 지역 변수는 함수가 실행되는 동안에만 존재하며, 이 과정에서 해당 값을 저장하기 위해 스택이나 힙에 지역 변수에 해당하는 공간이 할당되며, 이 변수는 함수가 끝날 때까지 함수에서 사용됩니다. , 클로저 패키지 내부 기능으로 인해 외부 기능은 끝이라고 볼 수 없습니다.

코드를 설명해 보겠습니다.

function fn1() {
 var obj = {name: 'hanzichi', age: 10};
}

function fn2() {
 var obj = {name:'hanzichi', age: 10};
 return obj;
}

var a = fn1();
var b = fn2();

코드가 어떻게 실행되는지 살펴보겠습니다. 먼저 fn1과 fn2라는 두 개의 함수가 정의됩니다. fn1이 호출되면 fn1의 환경에 들어가면 {name: 'hanzichi', age: 10} 객체를 저장하기 위한 메모리가 열리고 호출이 수행됩니다. fn1 환경이 완료되면 이 메모리 블록은 fn2 호출 프로세스 중에 js 엔진의 가비지 수집기에 의해 자동으로 해제됩니다. 반환된 객체는 전역 변수 b에 의해 지정되므로 이 메모리 블록은 석방되지 마십시오.

여기서 질문이 생깁니다. 어떤 변수가 쓸모없나요? 따라서 가비지 수집기는 어떤 변수가 쓸모 없는지 추적하고 향후 메모리 회수를 준비하기 위해 더 이상 유용하지 않은 변수를 표시해야 합니다. 사용되지 않는 변수를 표시하는 데 사용되는 전략은 구현마다 다를 수 있지만 일반적으로 표시 청소와 참조 계산이라는 두 가지 구현이 있습니다. 참조 카운팅은 덜 일반적으로 사용되며 표시 및 스윕이 더 일반적으로 사용됩니다.

2. 마크 지우기

JS에서 가장 일반적으로 사용되는 가비지 수집 방법은 마크 지우기입니다. 예를 들어 함수에서 변수를 선언하여 변수가 환경에 들어가면 해당 변수는 "환경에 들어갔습니다."로 표시됩니다. 논리적으로 환경에 진입하는 변수가 차지하는 메모리는 실행 흐름이 해당 환경에 진입할 때마다 사용될 수 있으므로 결코 해제될 수 없습니다. 그리고 변수가 환경을 떠나면 "환경을 떠나는 것"으로 표시됩니다.

function test(){
 var a = 10 ; //被标记 ,进入环境 
 var b = 20 ; //被标记 ,进入环境
}
test(); //执行完毕 之后 a、b又被标离开环境,被回收。

가비지 컬렉터가 실행되면 메모리에 저장된 모든 변수를 표시합니다(물론 모든 표시 방법을 사용할 수 있습니다). 그런 다음 환경 내 변수와 환경 내 변수가 참조하는 변수의 태그(클로저)를 제거합니다. 이후에 표시된 변수는 환경 내의 변수가 더 이상 해당 변수에 접근할 수 없으므로 삭제할 변수로 간주됩니다. 마지막으로 가비지 수집기는 메모리 정리 작업을 완료하여 표시된 값을 삭제하고 해당 값이 차지하는 메모리 공간을 회수합니다.

지금까지 IE, Firefox, Opera, Chrome 및 Safari의 js 구현은 모두 표시 및 청소 가비지 수집 전략 또는 유사한 전략을 사용하지만 가비지 수집 시간 간격은 다릅니다.

3. 참조 횟수

참조 카운팅의 의미는 각 값이 참조되는 횟수를 추적하는 것입니다. 변수가 선언되고 참조 유형 값이 변수에 할당되면 이 값에 대한 참조 수는 1입니다. 동일한 값이 다른 변수에 할당되면 해당 값의 참조 카운트가 1 증가합니다. 반대로, 이 값에 대한 참조를 포함하는 변수가 다른 값을 얻으면 이 값에 대한 참조 수가 1씩 감소합니다. 이 값에 대한 참조 횟수가 0이 되면 더 이상 이 값에 접근할 수 없다는 뜻이므로, 차지하는 메모리 공간을 회수할 수 있습니다. 이런 식으로 가비지 수집기가 다음에 실행될 때 참조 횟수가 0인 해당 값이 차지한 메모리를 해제합니다.

function test(){
 var a = {} ; //a的引用次数为0 
 var b = a ; //a的引用次数加1,为1 
 var c =a; //a的引用次数再加1,为2
 var b ={}; //a的引用次数减1,为1
}

Netscape Navigator3는 참조 카운팅 전략을 사용한 최초의 브라우저였지만 곧 순환 참조라는 심각한 문제에 직면했습니다. 순환 참조는 객체 A에 객체 B에 대한 포인터가 포함되어 있고 객체 B에도 객체 A에 대한 참조가 포함되어 있음을 의미합니다.

function fn() {
 var a = {};
 var b = {};
 a.pro = b;
 b.pro = a;
}

fn();

  以上代码a和b的引用次数都是2,fn()执行完毕后,两个对象都已经离开环境,在标记清除方式下是没有问题的,但是在引用计数策略下,因为a和b的引用次数不为0,所以不会被垃圾回收器回收内存,如果fn函数被大量调用,就会造成内存泄露。在IE7与IE8上,内存直线上升。

我们知道,IE中有一部分对象并不是原生js对象。例如,其内存泄露DOM和BOM中的对象就是使用C++以COM对象的形式实现的,而COM对象的垃圾回收机制采用的就是引用计数策略。因此,即使IE的js引擎采用标记清除策略来实现,但js访问的COM对象依然是基于引用计数策略的。换句话说,只要在IE中涉及COM对象,就会存在循环引用的问题。

var element = document.getElementById("some_element");
var myObject = new Object();
myObject.e = element;
element.o = myObject;

  这个例子在一个DOM元素(element)与一个原生js对象(myObject)之间创建了循环引用。其中,变量myObject有一个名为element的属性指向element对象;而变量element也有一个属性名为o回指myObject。由于存在这个循环引用,即使例子中的DOM从页面中移除,它也永远不会被回收。

看上面的例子,有同学回觉得太弱了,谁会做这样无聊的事情,其实我们是不是就在做

window.onload=function outerFunction(){
 var obj = document.getElementById("element");
 obj.onclick=function innerFunction(){};
};

这段代码看起来没什么问题,但是obj引用了document.getElementById(“element”),而document.getElementById(“element”)的onclick方法会引用外部环境中德变量,自然也包括obj,是不是很隐蔽啊。

解决办法

最简单的方式就是自己手工解除循环引用,比如刚才的函数可以这样

myObject.element = null;
element.o = null;


window.onload=function outerFunction(){
 var obj = document.getElementById("element");
 obj.onclick=function innerFunction(){};
 obj=null;
};

변수를 null로 설정한다는 것은 변수와 이전에 참조한 값 사이의 연결이 끊어진다는 의미입니다. 다음에 가비지 수집기가 실행될 때 이러한 값은 제거되고 해당 값이 차지하는 메모리는 회수됩니다.

IE9에는 순환 참조로 인한 Dom 메모리 누수 문제가 없다는 점에 유의해야 합니다. Microsoft가 최적화를 수행했거나 Dom을 재활용하는 방식이 변경되었을 수 있습니다

4. 메모리 관리

1. 가비지 수집은 언제 시작되나요?

가비지 수집기는 주기적으로 실행됩니다. 많은 메모리가 할당되면 재활용 작업이 매우 어려워집니다. 가비지 수집 간격을 결정하는 것은 생각해 볼 만한 문제입니다. IE6의 가비지 수집은 메모리 할당량에 따라 실행됩니다. 환경에 256개의 변수, 4096개의 개체 또는 64k 문자열이 있으면 가비지 수집기가 실행됩니다. 매우 과학적인 것처럼 보이며 단락을 누를 필요가 없습니다. 매번 한 번씩만 호출되는데, 때로는 필요하지 않을 때도 있는데, 필요할 때 호출하면 좋지 않을까요? 하지만 환경에 너무 많은 변수가 있고 스크립트가 지금 너무 복잡하다면 이는 정상입니다. 결과적으로 가비지 수집기가 항상 작동하므로 브라우저가 재생할 수 없게 됩니다.

Microsoft는 IE7에서 조정을 수행했습니다. 트리거 조건은 더 이상 고정되지 않고 동적으로 수정됩니다. 가비지 수집기에 의해 복구된 메모리 할당량이 메모리 점유량의 15% 미만인 경우. 프로그램에서는 대부분의 메모리를 재활용할 수 없다는 의미입니다. 이때 설정된 가비지 수집 트리거 조건이 너무 민감합니다. 이때 재활용된 메모리가 85%보다 높으면 대부분의 메모리를 의미합니다. 메모리는 오래 전에 정리되어야 합니다. 이때 트리거 조건을 다시 설정하십시오. 이렇게 하면 가비지 수집 기능이 훨씬 쉬워집니다

2. 합리적인 GC 계획

1) Javascript 엔진의 기본 GC 솔루션은 (간단한 GC): Mark and Sweep, 즉

  • (1) 접근 가능한 모든 객체를 순회합니다.
  • (2) 더 이상 접근할 수 없는 개체를 재활용합니다.

2) GC 결함

다른 언어와 마찬가지로 JavaScript의 GC 전략도 문제를 피할 수 없습니다. GC 중에는 보안상의 이유로 다른 작업에 대한 응답이 중지됩니다. Javascript의 GC는 100ms 이상이면 일반적인 응용에서는 괜찮지만 상대적으로 높은 연속성을 요구하는 JS 게임이나 애니메이션 응용에서는 문제가 됩니다. 이것이 바로 새 엔진이 최적화해야 하는 것입니다. 즉, GC로 인한 응답의 긴 일시 중지를 방지하는 것입니다.

3) GC 최적화 전략

David 삼촌은 주로 2가지 최적화 계획을 소개했는데, 이 두 가지가 가장 중요한 최적화 계획이기도 합니다.

(1) Generation GC(세대 GC)
이는 Java 재활용 전략의 아이디어와 일치합니다. 목적은 "임시" 개체와 "지속적" 개체를 구별하는 것입니다. 더 많은 "임시 개체" 영역(젊은 세대)과 덜 "지속적 개체" 영역(종신 세대)을 재활용하여 매번 통과해야 하는 개체 수를 줄이는 것입니다. , 이를 통해 각 GC 시간 소모를 줄입니다. 사진과 같이:

여기서 추가해야 할 점은 tenured 세대 개체의 경우 추가 오버헤드가 있다는 것입니다. 즉, 젊은 세대에서 tenured 세대로 마이그레이션하는 것입니다. 또한 참조되는 경우 참조 지점도 수정해야 합니다.

(2) 증분 GC
이 솔루션의 아이디어는 매우 간단합니다. 즉, "매번 조금씩 처리하고 다음 번에는 조금씩 처리하는 방식"입니다. 사진과 같이:

이 솔루션은 시간은 짧지만 중단이 많고 컨텍스트 전환이 자주 발생하는 문제를 가져옵니다.

각 솔루션에는 적용 가능한 시나리오와 단점이 있으므로 실제 적용에서는 실제 상황에 따라 솔루션이 선택됩니다.

예: (객체/들) 비율이 낮을 때 인터럽트 실행 GC 빈도가 낮고 단순 GC가 낮을 때 많은 수의 객체가 오랫동안 "생존"하면 세대별 이점이 있습니다. 처리가 별로네요.

참고:

위 내용은 JavaScript의 가비지 수집 메커니즘과 메모리 관리에 대한 내용입니다. 모든 사람의 학습에 도움이 되기를 바랍니다.

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.