JavaScript对内存分配及管理机制详细解析


Posted in Javascript onNovember 11, 2013

你可能听说过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 = o; // o2 引用 o

return “azerty”;
}

f();
// o o2两个对象构成了循环引用
// 当函数执行完毕的时候,他们就被关在了f的作用域里面,没有外面的人可以使用他们
// 所以按理说,他们已经没有存在价值了,需要被垃圾回收,释放内存
// 然而,他们的引用计数都不为“0”
// 所以在这种引用计数的机制下,他们没有被回收

实际例子
在IE6,7版本的浏览器中,就是使用的引用计数机制。因此,下面的代码在IE6,7中可以稳稳地发生内存泄漏

var div = document.createElement("div");
div.onclick = function(){
  doSomething();
}; // div的onclick属性,会引用 function
// 然而这个 function 反过来又引用了这个div,因为div在handler的作用域里面。
// 造成上述循环引用,导致内存泄漏。标记清除算法

这种算法把“可以回收”定义成“对象不可达”,即访问不到。

这种算法,会定义一个“根”,并且定期地从“根”出发,找出“根”下面的所有对象,看能不能从“根”找到一条路径引用到这个对象。从不同的“根”出发,垃圾回收程序就可以区分所有对象是不是“不可达”的,当对象“不可达”时候,便被回收。

这种算法比引用计数算法要好些。因为 “一个对象的引用计数是0”可以推出“这个对象不可达”,逆命题则为假。也就是说这种算法扩充了垃圾回收的范围。

循环引用不再是困扰
在上面的循环引用例子中,当函数返回时,o 和 o2都已经不再被任何人引用,也就是“不可达”了,便顺理成章地被垃圾回收掉了。

局限:对象需要明确的“不可达”
虽然说是局限,然而这种情况在实际当中很少发生,因此很少有人关注这一点。

Javascript 相关文章推荐
List the Stored Procedures in a SQL Server database
Jun 20 Javascript
ie和firefox不兼容的解决方法集合
Apr 28 Javascript
Javascript attachEvent传递参数的办法
Dec 14 Javascript
jquery 笔记 事件
Nov 02 Javascript
用Javascript实现Windows任务管理器的代码
Mar 27 Javascript
jquery 提交值不为空的元素示例代码
May 10 Javascript
jquery实现textarea 高度自适应
Mar 11 Javascript
很不错的两款Bootstrap Icon图标选择组件
Jan 28 Javascript
Vue.js结合Ueditor富文本编辑器的实例代码
Jul 11 Javascript
使用Vue.js和Flask来构建一个单页的App的示例
Mar 21 Javascript
node.js 基于cheerio的爬虫工具的实现(需要登录权限的爬虫工具)
Apr 10 Javascript
JS sort方法基于数组对象属性值排序
Jul 10 Javascript
javascript内存管理详细解析
Nov 11 #Javascript
PHP abstract与interface之间的区别
Nov 11 #Javascript
js 一个关于图片onload加载的事
Nov 10 #Javascript
javascript陷阱 一不小心你就中招了(字符运算)
Nov 10 #Javascript
全面理解面向对象的 JavaScript(来自ibm)
Nov 10 #Javascript
面向对象设计模式的核心法则
Nov 10 #Javascript
JavaScript 语言基础知识点总结(思维导图)
Nov 10 #Javascript
You might like
教你IIS6的PHP最佳配置方法
2006/09/05 PHP
PHP 通过Socket收发十六进制数据的实现代码
2013/08/16 PHP
ECSHOP在PHP5.5及高版本上报错的解决方法
2015/08/31 PHP
[全兼容哦]--实用、简洁、炫酷的页面转入效果loing
2007/05/07 Javascript
javascript 兼容鼠标滚轮事件
2009/04/07 Javascript
基于Jquery的回车成tab焦点切换效果代码(Enter To Tab )
2010/11/14 Javascript
jQuery学习笔记之 Ajax操作篇(一) - 数据加载
2014/06/23 Javascript
微信小程序遇到修改数据后页面不渲染的问题解决
2017/03/09 Javascript
vue 实现在函数中触发路由跳转的示例
2018/09/01 Javascript
[00:58]PWL开团时刻DAY5——十人开雾0换5
2020/11/04 DOTA
在Python中关于中文编码问题的处理建议
2015/04/08 Python
机器学习python实战之手写数字识别
2017/11/01 Python
Python编程求解二叉树中和为某一值的路径代码示例
2018/01/04 Python
Flask框架配置与调试操作示例
2018/07/23 Python
Python并发之多进程的方法实例代码
2018/08/15 Python
python+PyQT实现系统桌面时钟
2020/06/16 Python
解决pycharm 远程调试 上传 helpers 卡住的问题
2019/06/27 Python
Python 计算任意两向量之间的夹角方法
2019/07/05 Python
python 实现在一张图中绘制一个小的子图方法
2019/07/07 Python
python从list列表中选出一个数和其对应的坐标方法
2019/07/20 Python
新版Pycharm中Matplotlib不会弹出独立的显示窗口的问题
2020/06/02 Python
Trip.com香港网站:Ctrip携程旗下,全球最大的网上旅游社之一
2016/08/01 全球购物
梅西酒窖:Macy’s Wine Cellar
2018/01/07 全球购物
viagogo意大利票务平台:演唱会、体育比赛、戏剧门票
2018/01/26 全球购物
世界上最大的高分辨率在线图片库:Alamy
2018/07/07 全球购物
广告业务员岗位职责
2014/02/06 职场文书
《王二小》教学反思
2014/02/27 职场文书
数学系毕业生求职信
2014/05/29 职场文书
电气自动化求职信
2014/06/24 职场文书
标准离婚协议书(2014版)
2014/10/05 职场文书
检讨书范文300字
2015/01/28 职场文书
教师学期个人总结
2015/02/11 职场文书
2015年世界无车日活动总结
2015/03/23 职场文书
幼儿园班级工作总结2015
2015/05/25 职场文书
《少年闰土》教学反思
2016/02/18 职场文书
Python自然语言处理之切分算法详解
2021/04/25 Python