Javascript 中的内存管理

译自MDN,Memory Management

简介

在底层语言中,比如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都已经不再被任何人引用,也就是“不可达”了,便顺理成章地被垃圾回收掉了。

局限:对象需要明确的“不可达”

虽然说是局限,然而这种情况在实际当中很少发生,因此很少有人关注这一点。

原文:

Although this is marked as a limitation, it is one that is rarely reached in practice which is why no one usually cares that much about garbage collection.

受英语水平限制,这一点没太理解,还是把原文搬过来好了。

参考

说明

以上大部分文字来自Memory Management。尽量在保证不出错的前提下,加上了一些个人的注释和理解。虽然被MDN标注了 This article is in need of a technical review.,但是对于理解垃圾回收还是有一点帮助的吧。

Javascript中的垃圾回收机制的更多相关文章

  1. JavaScript中的垃圾回收机制与内存泄露

    什么是内存泄露? 任何编程语言,在运行时都需要使用到内存,比如在一个函数中, var arr = [1, 2, 3, 4, 5]; 这么一个数组,就需要内存. 但是,在使用了这些内存之后, 如果后面他 ...

  2. JavaScript具有自动垃圾回收机制

    JavaScript具有自动垃圾回收机制 原理: 找出那些不再继续使用的变量,然后释放其占用的内存.   正常的生命周期:     局部变量指在函数执行的过程中存在.而在这个过程中,会为局部变量在栈或 ...

  3. JavaScript中的垃圾回收和内存泄漏

    摘要: JS内存管理. 作者:浪里行舟 Fundebug经授权转载,版权归原作者所有. 前言 程序的运行需要内存.只要程序提出要求,操作系统或者运行时就必须供给内存.所谓的内存泄漏简单来说是不再用到的 ...

  4. 浅谈Chrome V8引擎中的垃圾回收机制

    垃圾回收器 JavaScript的垃圾回收器 JavaScript使用垃圾回收机制来自动管理内存.垃圾回收是一把双刃剑,其好处是可以大幅简化程序的内存管理代码,降低程序员的负担,减少因 长时间运转而带 ...

  5. 浅谈V8引擎中的垃圾回收机制

    最近在看<深入浅出nodejs>关于V8垃圾回收机制的章节,转自:http://blog.segmentfault.com/skyinlayer/1190000000440270 这篇文章 ...

  6. JavaScript学习系列6 -- JavaScript中的垃圾回收(内存释放)

    程序开发中,涉及到的内存生命周期基本是一样的,分为以下三步 1. 分配需要的内存 2. 使用分配到的内存 3. 释放其内存    ----什么时候释放内存,以及需要释放哪些变量的内存, 就是垃圾回收机 ...

  7. Chrome V8系列--浅析Chrome V8引擎中的垃圾回收机制和内存泄露优化策略

    V8 实现了准确式 GC,GC 算法采用了分代式垃圾回收机制.因此,V8 将内存(堆)分为新生代和老生代两部分. 一.前言 V8的垃圾回收机制:JavaScript使用垃圾回收机制来自动管理内存.垃圾 ...

  8. Python中的垃圾回收机制

    Python的垃圾回收机制 引子: 我们定义变量会申请内存空间来存放变量的值,而内存的容量是有限的,当一个变量值没有用了(简称垃圾)就应该将其占用的内存给回收掉,而变量名是访问到变量值的唯一方式,所以 ...

  9. Java中的垃圾回收机制

    1. 垃圾回收的意义 在C++中,对象所占的内存在程序结束运行之前一直被占用,在明确释放之前不能分配给其它对象:而在Java中,当没有对象引用指向原先分配给某个对象的内存时,该内存便成为垃圾.JVM的 ...

随机推荐

  1. 按键精灵对APP自动化测试(上)

    简单介绍下应用背景:测试安卓app时发现重复点击某一按钮的时候会出现报错,开发修复后提交测试.如果采用手动点击按钮,效率不高,在领导提示下使用按键精灵实现自动操作. 一.       安卓手机按键精灵 ...

  2. 基于Activiti工作流引擎实现的请假审核流程

    概要 本文档介绍的是某商用中集成的Activiti工作流的部署及使用,该框架用的Activiti版本为5.19.0.本文档中主要以一个请假流程为例子进行说明,该例子的流程图如下: 这是一个可以正常运作 ...

  3. 结对&词频统计

    结对编程 Pathner 濮成林(博客:http://www.cnblogs.com/charliePU/) 1.词频统计 环境依赖: 开发环境.myeclipse 2013, jdk1.7.0_04 ...

  4. spring-boot随笔

    配置了spring-boot-starter-web的依赖后,会自动添加tomcat和spring mvc的依赖,那么spring boot 会对tomcat和spring mvc进行自动配置 < ...

  5. Leetcode题库——38.报数

    @author: ZZQ @software: PyCharm @file: countAndSay.py @time: 2018/11/9 14:07 说明:报数序列是一个整数序列,按照其中的整数的 ...

  6. Locality Sensitive Hashing,LSH

    1. 基本思想 局部敏感(Locality Senstitive):即空间中距离较近的点映射后发生冲突的概率高,空间中距离较远的点映射后发生冲突的概率低. 局部敏感哈希的基本思想类似于一种空间域转换思 ...

  7. 个人作业3——个人总结AlPha阶段

    一.Alpha版本的总结 1.感受 Alpha版本已经结束了,回顾整个过程,我最大的遗憾就是项目完成得不是很理想,同时觉得自己做得不够多.不够好. 2.我做了哪些工作 数据库的连接,部分团队博客:部分 ...

  8. C#简述(二)

    详情请参考:http://www.runoob.com/csharp/csharp-operators.html 1.C# 运算符 算术运算符 关系运算符 逻辑运算符 位运算符 赋值运算符 其他运算符 ...

  9. 【壹拾壹周】final用户调查

    组名: 新蜂组长: 武志远组员: 宫成荣 谢孝淼 杨柳 李峤项目名称:java俄罗斯方块NEO 问卷星由宫成荣同学发布: 温馨提示:点击右键,在新标签中打开图片,单击图片即可放大.或者使用按住ctrl ...

  10. CMS垃圾收集器与G1收集器

    1.CMS收集器 CMS收集器是一种以获取最短回收停顿时间为目标的收集器.基于“标记-清除”算法实现,它的运作过程如下: 1)初始标记 2)并发标记 3)重新标记 4)并发清除 初始标记.从新标记这两 ...