>웹 프론트엔드 >JS 튜토리얼 >JavaScript 메모리 할당 및 관리 메커니즘에 대한 상세 분석_기본 지식

JavaScript 메모리 할당 및 관리 메커니즘에 대한 상세 분석_기본 지식

WBOY
WBOY원래의
2016-05-16 17:16:281272검색

你可能听说过JAVA、.NET、PHP这些语言有垃圾回收的内存管理机制,但是很少会听到JavaScript也有自己的内存管理机制,JavaScript同样有着类似的垃圾回收功能。本文主要讲述了JavaScript的垃圾回收原理和具体的过程。

简介
在底层语言中,比如C,有专门的内存管理机制,比如malloc() 和 free()。而Javascript是有垃圾回收(garbage collection)机制的,也就是说JS解释器会自动分配和回收内存。这样就有人觉得,我用的是高级语言,就不用关心内存管理了,其实这是不对的。

内存的生命周期
尽管语言不尽相同,而每种语言中内存的生命周期都是相似的:

1.当需要的时候分配内存
2.对内存进行读写操作
3.当上面分配的内存不再需要的时候,将他们释放掉
对于1,2两步,几乎所有语言操作起来都是明确地或者说很直观,没什么好说的。而在像Javascript一样的高级语言中,第三步操作就显得不那么直观。

Javascript中分配内存空间
变量初始化
当变量初始化的时候,Javascript会自动分配相应的内存空间(注:这里MDN上关于这里用的是Value initialization,到底是声明,还是在赋值时候分配空间,还要再学习一下)

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'; //个人补充,未考证,这里会为someElement分配空间,如注释所说,为对象分配空间
}, false);

函数调用时候分配空间
有的函数调用,会产生上面说的那种 为对象分配空间

var d = new Date();
var e = document.createElement('div'); // allocates an DOM element还有下面这种

var s = “azerty”;
var s2 = s.substr(0, 3); // s2 is a new string
// 由于Javascript中字符串是不可变的,所以Javascript也许并没有为s2中的字符串分配新空间,而是只存了[0, 3]的区间(用来索引)

var a = ["ouais ouais", "nan nan"];
var a2 = ["generation", "nan nan"];
var a3 = a.concat(a2); // 新的空间来存储数组a3

操作变量值
没什么好说的,读、写、函数调用。

内存不再被使用时,将它们释放掉
许多内存管理机制的问题都出现在这里。最麻烦的问题是确认“这块内存空间已经不需要了”。这往往需要程序员告知,这个程序中,这块内存已经不需要了,你们回收吧。

而高级语言解释器中嵌入了一个叫做“垃圾回收(garbage collector)”的工具,用来跟踪内存分配和使用情况,以便在它们不需要的时候将其自动回收。然而有个问题,一块内存空间是不是还有用,是具有不确定性的,也就是说,这个是没法用算法精确算出来的。

垃圾回收
如上所述原因,垃圾回收机制采取了一种有限的解决方案来处理上面的不确定性问题。下面介绍集中垃圾回收算法的思想以及相应的局限:

引用
这种方法,用到了一种引用的思想。当a能访问A时,就说A引用了a(不论是直接还是间接的)。比如,一个Javascript对象会引用他的原型(间接引用)和它的各个属性(直接引用)。

这种情形下,对象就被扩展的更广义了,在原生对象的基础上,还包含了函数的作用域链(或者全局的词法作用域)。

引用计数
这种方法是最拿衣服(naive)的垃圾回收算法。它把“可以回收”的标准定义为“没有其他人引用这个对象”(原文:This algorithm reduces the definition of “an object is not needed anymore” to “an object has no other object referencing to it”)。也就是说,只有当对象没有被引用的时候,才会被当作垃圾回收掉。

举个例子
var o = { // 称之为外层对象
a: { //称之为内层对象
b:2
}
}; //  创建了两个对象 内层对象作为外层对象的属性而被引用
// 而外层对象被变量o引用
// 显然,没有人会被垃圾回收

var o2 = o; // o2는 위에서 언급한 외부 객체도 참조합니다. 다행스럽게도 이제 외부 객체의 참조 카운트는 '2'입니다(o와 o2에 의해 참조됨)
o = 1; // 이제 o는 더 이상 외부 객체를 참조하지 않고 o2만 참조하며 참조 카운트는 다음과 같습니다. '1'

var oa = o2.a; // oa는 내부 객체를 참조합니다
// 이제 내부 객체는 외부 객체의 속성과 oa에 의해 참조되며 참조 횟수는 '2'입니다.

o2 = "yo"; // 이제 o2는 더 이상 외부 개체를 참조하지 않으며 외부 개체 참조 횟수는 "0"입니다.
// 이는 외부 개체가 "가비지 수집"될 수 있음을 의미합니다.
// 하지만 내부 개체는 여전히 oa에서 참조하고 있으므로 재활용되지 않았습니다. (개인 메모: 여기서는 종료 힌트가 있습니다.)

oa = null; // 이제 oa는 내부 객체를 참조하지 않습니다
// 내부 객체도 가비지 수집됩니다

제한: ​​순환 참조

아래 코드를 보세요:

function f(){
var o = {};
var o2 = {};
o.a = o2; // o 참조 o2
o2.a = o2; 인용

“azerty”를 반환합니다.
}

f();
// o o2 두 객체는 ​​순환 참조를 형성합니다
// 함수가 실행되면 f의 범위에 잠겨 외부에서는 누구도 사용할 수 없습니다
// 따라서 기존 값이 없으며 가비지 수집되어 메모리를 해제해야 하는 이유가 있습니다.
// 그러나 참조 카운트는 "0"이 아닙니다.
// 따라서 여기 이 참조 카운팅 메커니즘이 적용됩니다. , 재활용되지 않습니다

실제 예
IE6 및 7 버전의 브라우저에서는 참조 카운팅 메커니즘이 사용됩니다. 따라서 다음 코드는 IE6 및 7에서 메모리 누수를 확실히 일으킬 수 있습니다

var div = document.createElement("div");
div.onclick = function(){
doSomething();
} // div의 onclick 속성은 함수를 참조합니다
// 그러나 이 함수는 div가 핸들러의 범위 내에 있기 때문에 이 div를 참조합니다.
// 위의 순환 참조가 발생하여 메모리 누수가 발생합니다. 표시 및 지우기 알고리즘

이 알고리즘은 "재활용 가능"을 "접근할 수 없는 개체", 즉 액세스할 수 없는 개체로 정의합니다.

이 알고리즘은 "루트"를 정의하고 "루트"에서 주기적으로 시작하여 "루트" 아래의 모든 개체를 찾아 "루트"에서 이 개체를 참조하는 경로를 찾을 수 있는지 확인합니다. 다른 "루트"에서 시작하여 가비지 수집 프로그램은 모든 개체가 "접근할 수 없음"인지 여부를 구별할 수 있습니다.

이 알고리즘은 참조 카운팅 알고리즘보다 좋습니다. "객체의 참조 횟수가 0이다"는 "이 객체는 도달할 수 없다"고 추론할 수 있고 반대 명제는 거짓이기 때문입니다. 즉, 이 알고리즘은 가비지 컬렉션의 범위를 확장합니다.

순환 참조는 더 이상 문제가 되지 않습니다
위의 순환 참조 예에서 함수가 반환되면 o와 o2는 더 이상 누구도 참조하지 않습니다. 즉, "접근할 수 없습니다". , 쓰레기에 의해 자연스럽게 수집되었습니다.

제한 사항: 개체는 명시적으로 "접근 불가능"해야 합니다.
제한 사항이지만 실제로는 이런 상황이 거의 발생하지 않으므로 이에 주의하는 사람이 거의 없습니다.

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