>웹 프론트엔드 >JS 튜토리얼 >JavaScript의 가비지 컬렉션 메커니즘에 대한 전반적인 이해_기본 지식

JavaScript의 가비지 컬렉션 메커니즘에 대한 전반적인 이해_기본 지식

亚连
亚连원래의
2018-05-19 16:39:30978검색

이제 JavaScript의 가비지 수집 메커니즘에 대한 포괄적인 이해를 제공하겠습니다. 이제 그것을 여러분과 공유하고 모든 사람에게 참고 자료로 제공하겠습니다.

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. Mark and Clear

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

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内存泄露问题,可能是微软做了优化,或者Dom的回收方式已经改变

四、内存管理

1、什么时候触发垃圾回收?

垃圾回收器周期性运行,如果分配的内存非常多,那么回收工作也会很艰巨,确定垃圾回收时间间隔就变成了一个值得思考的问题。IE6的垃圾回收是根据内存分配量运行的,当环境中存在256个变量、4096个对象、64k的字符串任意一种情况的时候就会触发垃圾回收器工作,看起来很科学,不用按一段时间就调用一次,有时候会没必要,这样按需调用不是很好吗?但是如果环境中就是有这么多变量等一直存在,现在脚本如此复杂,很正常,那么结果就是垃圾回收器一直在工作,这样浏览器就没法儿玩儿了。

微软在IE7中做了调整,触发条件不再是固定的,而是动态修改的,初始值和IE6相同,如果垃圾回收器回收的内存分配量低于程序占用内存的15%,说明大部分内存不可被回收,设的垃圾回收触发条件过于敏感,这时候把临街条件翻倍,如果回收的内存高于85%,说明大部分内存早就该清理了,这时候把触发条件置回。这样就使垃圾回收工作职能了很多

2、合理的GC方案

1)、Javascript引擎基础GC方案是(simple GC):mark and sweep(标记清除),即:

  • (1)遍历所有可访问的对象。

  • (2)回收已不可访问的对象。

2)、GC的缺陷

和其他语言一样,javascript的GC策略也无法避免一个问题:GC时,停止响应其他操作,这是为了安全考虑。而Javascript的GC在100ms甚至以上,对一般的应用还好,但对于JS游戏,动画对连贯性要求比较高的应用,就麻烦了。这就是新引擎需要优化的点:避免GC造成的长时间停止响应。

3)、GC优化策略

David大叔主要介绍了2个优化方案,而这也是最主要的2个优化方案了:

(1)分代回收(Generation GC)

这个和Java回收策略思想是一致的。目的是通过区分“临时”与“持久”对象;多回收“临时对象”区(young generation),少回收“持久对象”区(tenured generation),减少每次需遍历的对象,从而减少每次GC的耗时。如图:

这里需要补充的是:对于tenured generation对象,有额外的开销:把它从young generation迁移到tenured generation,另外,如果被引用了,那引用的指向也需要修改。

(2)增量GC
这个方案的思想很简单,就是“每次处理一点,下次再处理一点,如此类推”。如图:

这种方案,虽然耗时短,但中断较多,带来了上下文切换频繁的问题。

因为每种方案都其适用场景和缺点,因此在实际应用中,会根据实际情况选择方案。

예: (객체/들) 비율이 낮을 때 인터럽트 실행 GC 빈도가 낮고 단순 GC가 낮을 때 많은 수의 객체가 오랫동안 "생존"하는 경우 세대 처리의 이점은 다음과 같습니다. 좋지 않다.

위 내용은 제가 여러분을 위해 정리한 내용입니다. 앞으로 도움이 되길 바랍니다.

관련 기사:

Javascript는 클래스와 객체를 생성합니다(그래픽 튜토리얼)

JavaScript 화살표 기능에 대한 자세한 설명(그래픽 튜토리얼)

JavaScript의 자세한 답변(그래픽 튜토리얼)

위 내용은 JavaScript의 가비지 컬렉션 메커니즘에 대한 전반적인 이해_기본 지식의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

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