最后一步在低级语言中很清晰,javascript会完成内

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

简介

介绍 低层次的语言,如C,具有低级别的内存管理命令,如:malloc,需要开发者手工释放内存。然而像javascript这样的高级语言情况则不同,对象创建的时候分配内存,当他们不在使用的时候内存会被自动回收,这个自动回收的过程被称为垃圾回收。因为垃圾回收的存在,让javascript等高级语言开发者产生了一个错误的认识,以为可以不用关心内存管理。 内存生命周期 不管什么样的编程语言,内存的生命周期基本上是一致的。 1.分配你需要的内存 2.使用他进行读写操作 3.当内存不需要的时候,释放资源 步骤1和步骤2对于所有语言都一样,能明显觉察到。至于步骤3,低级别语言需要开发者显式执行。而对于像javascript这样的高级语言,这部分操作是交给解析器完成的,所以你不会觉察到。 javascript中的分配操作 值的初始化 在为变量赋值的时候,javascript会完成内存的分配工作。 复制代码 代码如下:var n = 123; // 为数字分配内存var s = "azerty"; // 为字符串分配内存var o = {a: 1,b: null}; // 为包含属性值的object对象分配内存var a = [1, null, "abra"]; // 为包含值的数组分配内存function f{return a + 2;} // 为函数分配内存// 函数表达式同样也是对象,存在分配内存的情况someElement.addEventListener{someElement.style.backgroundColor = 'blue';}, false);通过函数调用完成分配 一些函数当执行完毕之后,同样存在对象分配的情况发生。 复制代码 代码如下:var d = new Date();var e = document.createElement; // 分配一个 DOM 元素一些方法会分配新值或者对象。 复制代码 代码如下:var s = "azerty";var s2 = s.substr; // s2 是一个新的字符串// 由于字符串是不变的,javascript会为[0, 3]范围的内容创建一个新的字符串var a = ["ouais ouais", "nan nan"];var a2 = ["generation", "nan nan"];var a3 = a.concat; // 把 a 和 a2 结合在一起,产生一个新的数组对值的使用 对值的使用,其实也就是对分配后的内存执行读写操作。这些操作包括:对变量或者对象的属性进行读写操作,或者向函数传递参数。 当不再需要的时候,释放内存 绝大多数内存管理的问题都发生在这个阶段。最难做的事情是,如何判定分配的内存不再需要。这往往需要开发者做出判定,程序在什么时候不再需要内存,并释放他所占资源。 高级语言的解析器中嵌入了一个叫做“垃圾收集器”的程序,他的工作是用来跟踪内存的分配和使用,判定内存是否被需要,在不再需要的时候执行资源释放操作。他只能获得一个近似值,因为判断一个内存是否被需要,这是个不确定的问题。 垃圾回收 正如上文所述,我们无法准确的做到自动判定“内存不再需要”。所以,垃圾回收对该问题的解决方案有局限性。本节将解释必要的概念,了解主要的垃圾收集算法和它们的局限性。 引用 垃圾回收中一个主要的概念是引用。在内存管理中,当一个对象无论是显式的还是隐式的使用了另外一个对象,我们就说他引用了另外一个对象。例如,javascript对象存在一个隐式的指向原型的引用,还有显式指向他的属性值的引用。 在这里,对象的概念超出了javascript传统意义上对象的概念,他还包括函数作用域和全局作用域。 使用引用计数算法的垃圾回收 下面要介绍的是一种最理想化的算法,引入了 “对象不再需要” 和 “没有其他对象引用该对象” 的概念。当该对象的引用指针变为0的时候,就认为他可以被回收。 例子: 复制代码 代码如下:var o = { a: {b:2}}; // 创建了两个对象. 一个对象引用,并把a作为他的属性// 该对象又被变量o引用// 很明显,这时没有对象能被回收var o2 = o; // 变量 o2 再次引用了该对象o = 1; // o 不再引用该对象,只有o2还在引用该对象var oa = o2.a; // oa引用 o2 的属性对象 a// 该对象被其他两个对象引用,分别是o2的属性a和oa变量o2 = "yo"; // 该对象已经不再被其他对象引用了,但是他的属性a任然被oa变量引用,所以他还不能被释放oa = null; // 现在属性a也不再被别的对象引用,该对象可以被回收了限制:循环 该算法有其局限性,当一个对象引用另外一个对象,当形成循环引用时,即时他们不再被需要了,垃圾收集器也不会回收他们。 复制代码 代码如下:function f(){var o = {};var o2 = {};o.a = o2; // o 引用 o2o2.a = o; // o2 引用 oreturn "azerty";}f();// 两个对象被创建,并形成相互引用// 函数调用结束之后,他们不会脱离函数作用域,虽然他们不会被使用,但不会被释放// 这是因为,引用计数的算法判定只要对象存在被引用的情况,那么就不能对其执行垃圾回收现实中的例子 ie6、7中,在dom对象上使用引用计数的算法,这里会存在内存泄露的问题。 复制代码 代码如下:var div = document.createElement;div.onclick = function;}; // div 通过 click 属性引用了事件处理程序// 当事件处理函数中访问了div变量的时候,会形成循环引用,将导致两个对象都不会被回收,造成内存泄露标记

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

低级语言,比如C,有低级的内存管理基元,想malloc。另一方面,JavaScript的内存基元在变量创建时分配,然后在他们不再被使用时“自动”释放。后者被称为垃圾回收。这个“自动”是混淆并给JavaScript开发者一个错觉:他们可以不用考虑内存管理。

  • 清除算法 他引入了“对象不再需要”和“对象不可访问”的概念。该算法假设有一系列的根对象(javascript中的根对象就是全局对象),每隔一段时间,垃圾收集器就会从根对象开始,遍历所以他引用的对象,然后再遍历引用对象引用的对象,以此类推。使用这种方式,垃圾收集器可以获得所有可访问的对象,回收那些不可访问的对象。 这种算法比之前的算法好些,0引用的对象会被设置为不可访问对象,同时他也避免了循环引用造成的困恼。 截止2012年,大多数现代浏览器使用的是这种“标记-清除算法”的垃圾回收器。JavaScript垃圾收集领域,在过去的几年改善了与之相关的算法,但是垃圾收集算法本身和“如何判定一个对象不再需要”并没有得以改善。 周期不再是一个问题 在第一个例子中,函数调用结束之后,这两个对象不会被全局对象引用,也不会被全局对象引用的对象引用。因此,他们会被javascript垃圾回收器标记为不可访问对象。这种事情同样也发生在第二个例子中,当div和事件处理函数被垃圾回收器标记为不可访问,他们就会被释放掉。 限制:对象需要明确的标记为不可访问 这种标记的方法存在局限,但是我们在编程中被没有接触到他,所以我们很少关心垃圾回收相关的内容。

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

内存生命周期

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

不管什么程序语言,内存生命周期基本一致:

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

1.分配你所需要的内存2.使用它3.当它不被使用时释放 ps:和“把大象装冰箱“一个意思

var n = 123; // 为数字分配空间var s = “azerty”; // 字符串

第一二部分过程在所有语言中都很清晰。最后一步在低级语言中很清晰,但是在像JavaScript等高级语言中,最后一步不清晰。

var o = {a: 1,b: null}; // 为对象和它包含的属性分配内存空间

JavaScript的内存分配

var a = [1, null, "abra"]; // 给数组和它里面的元素分配空间

变量初始化

function f{return a + 2;} // 为函数分配空间

为了不让程序员为分配费心,JavaScript在定义变量时完成内存分配。

// 函数有时也会为分配对象空间someElement.addEventListener{someElement.style.backgroundColor = ‘blue'; //个人补充,未考证,这里会为someElement分配空间,如注释所说,为对象分配空间}, false);

复制代码 代码如下:var n = 123; // 给数值变量分配内存var s = "azerty"; // 给字符型

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

var o = { a: 1, b: null}; // 为对象及其包含变量分配内存

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

var a = [1, null, "abra"]; // 为数组及其包含变量分配内存{ return a + 2;} // 为函数分配内存

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

// 函数表达式也能分配一个对象someElement.addEventListener{ someElement.style.backgroundColor = 'blue';}, false);

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

通过函数调用的内存分配

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

有些函数调用结果是分配对象内存:复制代码 代码如下:var d = new Date();var e = document.createElement; //分配一个DOM元素

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

有些方法分配新变量或者新对象:

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

复制代码 代码如下:var s = "azerty";var s2 = s.substr; // s2 is a new string//因为string是不变量,JavaScript可能没有分配内存,但只是存储了0-3的范围。

本文由必威发布于必威-运维,转载请注明出处:最后一步在低级语言中很清晰,javascript会完成内

TAG标签:
Ctrl+D 将本页面保存为书签,全面了解最新资讯,方便快捷。