javascript内存管理详细解析

介绍

低层次的语言,如C,具有低级别的内存管理命令,如:malloc()和free(),需要开发者手工释放内存。然而像javascript这样的高级语言情况则不同,对象(objects, strings 等)创建的时候分配内存,当他们不在使用的时候内存会被自动回收,这个自动回收的过程被称为垃圾回收。因为垃圾回收的存在,让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(a){
  return a + 2;
} // 为函数分配内存(函数是可调用的对象)

// 函数表达式同样也是对象,存在分配内存的情况
someElement.addEventListener('click', function(){
  someElement.style.backgroundColor = 'blue';
}, false);

通过函数调用完成分配

一些函数当执行完毕之后,同样存在对象分配的情况发生。

复制代码 代码如下:

var d = new Date();
var e = document.createElement('div'); // 分配一个 DOM 元素

一些方法会分配新值或者对象。
复制代码 代码如下:

var s = "azerty";
var s2 = s.substr(0, 3); // s2 是一个新的字符串
// 由于字符串是不变的,javascript会为[0, 3]范围的内容创建一个新的字符串

var a = ["ouais ouais", "nan nan"];
var a2 = ["generation", "nan nan"];
var a3 = a.concat(a2); // 把 a 和 a2 结合在一起,产生一个新的数组

对值的使用

对值的使用,其实也就是对分配后的内存执行读写操作。这些操作包括:对变量或者对象的属性进行读写操作,或者向函数传递参数。

当不再需要的时候,释放内存

绝大多数内存管理的问题都发生在这个阶段。最难做的事情是,如何判定分配的内存不再需要。这往往需要开发者做出判定,程序在什么时候不再需要内存,并释放他所占资源。

高级语言的解析器中嵌入了一个叫做“垃圾收集器”的程序,他的工作是用来跟踪内存的分配和使用,判定内存是否被需要,在不再需要的时候执行资源释放操作。他只能获得一个近似值,因为判断一个内存是否被需要,这是个不确定的问题(不能通过一种算法解决)。

垃圾回收

正如上文所述,我们无法准确的做到自动判定“内存不再需要”。所以,垃圾回收对该问题的解决方案有局限性。本节将解释必要的概念,了解主要的垃圾收集算法和它们的局限性。

引用

垃圾回收中一个主要的概念是引用。在内存管理中,当一个对象无论是显式的还是隐式的使用了另外一个对象,我们就说他引用了另外一个对象。例如,javascript对象存在一个隐式的指向原型的引用,还有显式指向他的属性值的引用。

在这里,对象的概念超出了javascript传统意义上对象的概念,他还包括函数作用域和全局作用域。

使用引用计数算法的垃圾回收

下面要介绍的是一种最理想化的算法,引入了 “对象不再需要” 和 “没有其他对象引用该对象” 的概念。当该对象的引用指针变为0的时候,就认为他可以被回收。

例子:

复制代码 代码如下:

var o = {
  a: {
    b:2
  }
}; // 创建了两个对象. 一个对象(a)被另外一个对象(o引用的对象)引用,并把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 引用 o2
  o2.a = o; // o2 引用 o

  return "azerty";
}

f();
// 两个对象被创建,并形成相互引用
// 函数调用结束之后,他们不会脱离函数作用域,虽然他们不会被使用,但不会被释放
// 这是因为,引用计数的算法判定只要对象存在被引用的情况,那么就不能对其执行垃圾回收

现实中的例子
ie6、7中,在dom对象上使用引用计数的算法,这里会存在内存泄露的问题。

复制代码 代码如下:

var div = document.createElement("div");
div.onclick = function(){
  doSomething();
}; // div 通过 click 属性引用了事件处理程序
// 当事件处理函数中访问了div变量的时候,会形成循环引用,将导致两个对象都不会被回收,造成内存泄露

标记 - 清除算法

他引入了“对象不再需要”和“对象不可访问(对象不可达)”的概念。该算法假设有一系列的根对象(javascript中的根对象就是全局对象),每隔一段时间,垃圾收集器就会从根对象开始,遍历所以他引用的对象,然后再遍历引用对象引用的对象,以此类推。使用这种方式,垃圾收集器可以获得所有可访问的对象,回收那些不可访问的对象。

这种算法比之前的算法好些,0引用的对象会被设置为不可访问对象,同时他也避免了循环引用造成的困恼。

截止2012年,大多数现代浏览器使用的是这种“标记-清除算法”的垃圾回收器。JavaScript垃圾收集领域(代/增量/并发/并行的垃圾收集),在过去的几年改善了与之相关的算法,但是垃圾收集算法本身(标记-清除算法)和“如何判定一个对象不再需要”并没有得以改善。

周期不再是一个问题

在第一个例子中,函数调用结束之后,这两个对象不会被全局对象引用,也不会被全局对象引用的对象引用。因此,他们会被javascript垃圾回收器标记为不可访问对象。这种事情同样也发生在第二个例子中,当div和事件处理函数被垃圾回收器标记为不可访问,他们就会被释放掉。

限制:对象需要明确的标记为不可访问

这种标记的方法存在局限,但是我们在编程中被没有接触到他,所以我们很少关心垃圾回收相关的内容。

本页内容版权归属为原作者,如有侵犯您的权益,请通知我们删除。
本文实例讲述了js实现简单选项卡与自动切换效果的方法。分享给大家供大家参考。具体分析如下: 这里再上篇《 js实现简单的可切换选项卡效果 》基础上,进一步实现可以自动切换的切换效果,用这种效果就可以做简单的焦点图了。 说明: 设置一个标识数字置为0,写一个每过几秒标识+1,执行切换效果的函数,然后执行。 当标识超过当前选项卡长度让标识置为0。 在鼠标移到选项卡的时候关闭定时器,鼠标移走的时候打开定时器。 !DOCTYPE htmlhtmlheadmeta charset="gb2312" /title无标
jquery代码: 复制代码 代码如下: $(function(){ $("#test").load("${contextPath}/notepad/toCreate.do"); } 加载 ${contextPath}/notepad/toCreate.do 页面到id为test的div中,加载完成之后,create页面中的js不会执行 这种方式没办法实现,换个思路: 复制代码 代码如下: div id="test" iframe name="testLoad"/iframe /div js事件: 复制代
本文实例讲述了JS设置网页图片vspace和hspace属性的方法。分享给大家供大家参考。具体分析如下: hspace可以以像素为单位,指定图像左边和右边的文字与图像之间的间距;vspace 值则是上面的下面的文字与图像之间的距离的像素数 !DOCTYPE htmlhtmlheadscriptfunction setSpace(){document.getElementById("compman").hspace="50";document.getElementById("compman").vspace

js实现图片漂浮效果的方法 - 2015-04-22 09:04:16

本文实例讲述了js实现图片漂浮效果的方法。分享给大家供大家参考。具体分析如下: 描述:打开网页就看到不停在飘动的图片,点击连接到其他页面;起到着重强调的效果! 复制代码 代码如下: html head meta http-equiv="Content-Type" content="text/html; charset=utf-8" titletest/title style type="text/css" #All{ width: 100%; height: 3000px;} /style /head b

jQuery简单实现禁用右键菜单 - 2015-04-22 09:04:16

代码非常简单,这里就不多废话了,直接上代码: 复制代码 代码如下: $(document).ready(function(){ $(document).bind("contextmenu",function(e){ return false; }); }); 5行代码搞定,简单吧,当然小伙伴们可以自由扩展下,可以实现屏蔽右键菜单的指定项。
方法一: 1、首先建立一个按钮,在后台将调用或处理的内容写入button_click中; 2、在前台写一个js函数,内容为document.getElementById("btn1").click(); 3、在前台或后台调用js函数,激发click事件,等于访问后台c#函数; 方法二:1、函数声明为public 后台代码(把public改成protected也可以) 以下是引用片段: 复制代码 代码如下: public string ss() { return("a"); } 2、在html里用%=fuc
复制代码 代码如下: !DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" HTML HEAD TITLE Demo /TITLE /HEAD BODY script src="jquery.js" type="text/javascript"/script script type="text/javascript" function clickImg(){ $.ajax({ type:"post", //提交类型 url:"demo.p
jCallout的下载地址: https://github.com/anupamsmaurya/jCallout 需要添加此引用 用户名一行的 html 代码是: 复制代码 代码如下: tr td class="columnName"用户名:/td tdinput id="hTbxUserName" class="needCheck" type="text"/span class="necessary"*/span/td /tr 然后在 js 中添加如下代码: 复制代码 代码如下: $('#hTbxUse
复制代码 代码如下: head titletest count down button/title script src="http://demo.jb51.net/jslib/jquery/jquery-1.5.1.min.js" type="text/javascript"/script script type="text/javascript" $(function () { $('#btn').click(function () { var count = 3; var countdown = s

node.js实现BigPipe详解 - 2015-04-22 09:04:11

BigPipe 是 Facebook 开发的优化网页加载速度的技术。网上几乎没有用 node.js 实现的文章,实际上,不止于 node.js,BigPipe 用其他语言的实现在网上都很少见。以至于这技术出现很久以后,我还以为就是整个网页的框架先发送完毕后,用另一个或几个 ajax 请求再请求页面内的模块。直到不久前,我才了解到原来 BigPipe 的核心概念就是只用一个 HTTP 请求,只是页面元素不按顺序发送而已。 了解了这个核心概念就好办了,得益于 node.js 的异步特性,很容易就可以用 nod