原文在https://developer.mozilla.org/en-US/docs/Web/JavaScript/Memory_Management.基本保持了平译,并在一些地方做了概念解释。(转载请注明出处)

介绍

高级语言,比如C,有底层的内存管理原语如malloc()和free()。但是对于Javascript来说,变量(string、object等等)被创建的时候分配内存,等变量不再被使用的时候,内存被"自动"释放。这个自动释放的过程叫做"垃圾回收"。“自动”这个词常使人困惑并且给了javascript(以及其他的高级语言)开发者他们不需要关心内存管理的印象。这是错误的。

内存生命周期

忽略具体的变成语言,内存的生命周期通常是这样的:

  1. 分配你需要的内存
  2. 使用他们(读、写操作)
  3. 当他们不再需要的时候释放他们

第二部分在所有的语言里都是明显的。第一和第三部分在低级语言里是明显的,但是在高级语言如JavaScript里是非常隐秘的。

JavaScript的内存分配
变量的初始化

为了不让程序的书写者操心内存分配问题,JavaScript随着变量的声明自动分配了内存。

var n = 123; //为1个数字分配了内存
var s = 'azerty';//为一个字符串分配了内存 var o = {
a:1,
b:null
};//为对象和里边的键值分配了内存 //(和对象一样),为数组和里边的元素分配了内存
var a = [1,null,'abra']; function f(a){
return a + 2;
}//为一个函数分配了内存(函数是可调用对象) //函数表达式也作为1个对象被分配了内存
//译者注:函数的表达式指的是下面的匿名函数的定义部分,JavaScript的函数可以作为函数的参数传入,因为他是一种特殊的对象
someElement.addEventListener('click', function() {
someElement.style.backgroundColor = 'blue';
}, false);
通过函数调用分配内存

一些函数调用的结果分配了内存

var d = new Date(); // 创建了一个Date对象,并分配了内存

var e = document.createElement('div'); // 创建了一个Dom元素,分配了内存

一些函数会分配新的变量或对象:

var s = 'azerty';
var s2 = s.substr(0, 3); // s2 是一个新的字符串
// 因为字符串是不可变对象,
// JavaScript 可能不会再为s2分配内存, 而仅仅让s2记录0-3这个区间 var a = ['ouais ouais', 'nan nan'];
var a2 = ['generation', 'nan nan'];
var a3 = a.concat(a2);
// 新的数组s3有4个元素,连接了a和a2
使用内存

使用内存基本上就是读写已分配的内存。比如读写变量的值,对象的属性,又或者给函数传递参数(译者:这里是说变量作为参数传递到函数的行为,就是使用这个变量内存的过程)。

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

这个阶段会产生许多内存管理的问题。最难的问题是找到什么时候“已分配的内存不再需要”,这要求开发者去检测什么地方的内存不再需要,然后释放它们。

高级语言嵌入了一个叫做“垃圾回收”的功能,它的作用是追踪内存分配,当发现一块已分配的内存不再需要的时候,自动释放它。这是一个近似的过程,因为“判断一段内存是否还需要保留”是不可判定的(undecidable .can't be solved by an algorithm.这里可以理解为,分配的内存还用不用只有程序员主观的知道,如果他忘了,会导致内存泄漏,如果程序员手贱,释放的早了,再访问这块内存就会导致内存访问错误。语言的垃圾回收机制更像是一个第三者,虽然他很聪明,算法很强大,很能揣测程序意图,但是毕竟不是本人)。

引用

垃圾回收算法的主要理论依据是一个叫“引用”的概念。在内存管理的上下文中,如果一个对象有权利访问另一个对象(的内存,无论是显式还是隐式),会让前者去引用后者。比如,Javascript对象有对他的原型隐式的引用,和对它属性显式的引用。

在内存管理的上下文中,“对象”的概念比通常的JavaScript的对象(Object)更为宽泛,甚至包含函数作用域,或者全局的词法作用域。

基于引用计数的垃圾回收

这是最简单的垃圾回收算法。这种算法将“一个内存中对象不再被需要”简化为"一个内存对象不再被别的对象引用"。如果一个对象被引用的数量为0,这个对象就被认为应该被回收了。(译者注:大多数此类算法都要求被管理的所有对象都有一个类似referenceCount的属性,标识自己被引用了多少次)。

例子:
var o = {
a:{
b:2
}
};
//2个对象被创建出来,1给被引用为对象的属性,然后外部的对象被变量o引用。显然他们都不会被回收
var o2 = o;//变量o2又引用了o引用的对象
o = 1;//现在变量o不再引用之前的对象,之前的对象只被o2引用了
var oa = o2.a;//现在作为对象属性的对象,又被变量oa引用了
o2 = 'yo';//现在最初被o引用的对象已经再被引用了,可以被回收,但是它的a属性还被oa引用,所以还不能被释放
oa = null;//现在作为a属性的对象也不再被引用了,最初被o引用的对象可以被回收释放了。
限制:循环引用

当引用出现循环,这种算法就会出现限制。下面的例子里,2个对象被创建出来,并且彼此被另一个引用,导致循环引用。函数执行完成后,他们会离开函数作用域,不再起作用了,可以被释放了。但是,引用计数算法认为他们2个都被引用了一次(函数接收后,变量o,o2会解掉对对象的引用),不能被回收。

function f() {
var o = {};
var o2 = {};
o.a = o2; // o引用的对象的属性a 引用 o2
o2.a = o; // o2引用的对象的属性a 引用 o return 'azerty';
} f();
真实的例子

IE6和IE7使用引用计数管理DOM对象,所以循环引用是常见的导致内存泄漏的错误。

var div;
window.onload = function() {
div = document.getElementById('myDivElement');
div.circularReference = div;
div.lotsOfData = new Array(10000).join('*');
};

这个例子里,dom元素"myDivElement"被自己的circularReference属性所引用(div.circularReference = div;)。如果这个属性没有明确的移除或者置为null,即使myDivElement这个元素从DOM树里移除了,引用计数垃圾收集器还将总是拥有至少一个这个元素的引用,并且一直存在于内存中。如果DOM元素持有大量数据(比如这个例子里的lotsOfData属性),这个数据消耗的内存将不会被释放回收。

基于标记-清除的垃圾回收

这个算法将"一个内存中对象不再被需要"简化为"一个内存对象不可达"。(译者注:原文是an object is unreachable。内存中的对象,如果程序无法通过一些线索,如引用关系找到它,它就没有存在的意义,是不可达的)。这种算法假定知道一组叫做根的对象,周期性的从这些“根”开始,查找到所有被根引用的对象,然后从这些对象开始,递归的查找下去。因此,从根开始查找的过程中,垃圾收集器将会找到所有可达的内存对象和不可达的内存对象。

这个算法比上一个要好,因为“一个被0引用”的内存对象可以推导出这个内存对象不可达,相反,出现循环引用的时候,反推却不成立。

截止2012年,所有的现代浏览器都搭载了标记-清除垃圾回收器。过去几年里,JavaScript 的垃圾回收领域(分代/增量/并发/并行垃圾回收)技术的更新实现了这种算法的进步,但是对垃圾回收算法本身:什么时候"一个内存对象不再需要"这个概念的处理,并没有多大进步。(原文:but not improvements over the garbage collection algorithm itself nor its reduction of the definition of when "an object is not needed anymore).

循环引用不再是问题

第一个例子里,函数结束后,2个对象不再被由全局对象可达的对象所引用,因此,他们被垃圾回收器认为是不可达的。

限制:内存对象需要被显式的设置为不可达

即使这被标注为一个限制,但是在实践中很少遇到,这也是为什么很少有人会经常关注垃圾收集的原因(说的是,在当前标记清除的技术下,程序员们不再关注垃圾回收这个问题了)。

垃圾回收gc --翻译的更多相关文章

  1. 垃圾回收GC:.Net自己主动内存管理 上(一)内存分配

    垃圾回收GC:.Net自己主动内存管理 上(一)内存分配 垃圾回收GC:.Net自己主动内存管理 上(一)内存分配 垃圾回收GC:.Net自己主动内存管理 上(二)内存算法 垃圾回收GC:.Net自己 ...

  2. 垃圾回收GC:.Net自己主动内存管理 上(二)内存算法

    垃圾回收GC:.Net自己主动内存管理 上(二)内存算法 垃圾回收GC:.Net自己主动内存管理 上(一)内存分配 垃圾回收GC:.Net自己主动内存管理 上(二)内存算法 垃圾回收GC:.Net自己 ...

  3. Java垃圾回收手册翻译 - 什么是垃圾回收

    Java垃圾回收手册翻译 - 什么是垃圾回收 初看之下,垃圾回收应该要做其名称之事 - 找到和丢掉垃圾.然而事实上它正好做着相反的事,垃圾回收会记录所有仍在使用中的对象,然后将其他标记为垃圾.谨记这点 ...

  4. 从C#垃圾回收(GC)机制中挖掘性能优化方案

    GC,Garbage Collect,中文意思就是垃圾回收,指的是系统中的内存的分配和回收管理.其对系统性能的影响是不可小觑的.今天就来说一下关于GC优化的东西,这里并不着重说概念和理论,主要说一些实 ...

  5. Java 垃圾回收(GC) 泛读

    Java 垃圾回收(GC) 泛读 文章地址:https://segmentfault.com/a/1190000008922319 0. 序言 带着问题去看待 垃圾回收(GC) 会比较好,一般来说主要 ...

  6. 类装饰器,元类,垃圾回收GC,内建属性、内建方法,集合,functools模块,常见模块

    '''''''''类装饰器'''class Test(): def __init__(self,func): print('---初始化---') print('func name is %s'%fu ...

  7. 性能测试三十五:jvm垃圾回收-GC

    垃圾回收-GC 三个问题 哪些内存需要回收? 什么时候回收? 如何回收? YoungGC和FullGC: 新生代引发的GC叫YoungGC 老年代引发的GC叫FullGC FullGC会引起整个Jvm ...

  8. 修改Tomcat的jvm的垃圾回收GC方式为CMS

    修改Tomcat的jvm的垃圾回收GC方式 cp $TOMCAT_HOME/bin/catalina.sh $TOMCAT_HOME/bin/catalina.sh.bak_20170815 vi $ ...

  9. 垃圾回收GC:.Net自己主动内存管理 上(三)终结器

    垃圾回收GC:.Net自己主动内存管理 上(三)终结器 垃圾回收GC:.Net自己主动内存管理 上(一)内存分配 垃圾回收GC:.Net自己主动内存管理 上(二)内存算法 垃圾回收GC:.Net自己主 ...

随机推荐

  1. OpenID简介

    OpenID 是一个以用户为中心的数字身份识别框架,它具有开放.分散性.OpenID 的创建基于这样一个概念:我们可以通过 URI (又叫 URL 或网站地址)来认证一个网站的唯一身份,同理,我们也可 ...

  2. 使用Navicat为Oracle新增用户

    步骤请参考帖子https://www.cnblogs.com/franson-2016/p/5925593.html 需要注意的是新增用户时不能使用小写,否则不能登录,之前新增一个小写的用户名,授予c ...

  3. k8s1.11.0安装、一个master、一个node、查看node名称是主机名、node是扩容进来的、带cadvisor监控服务

    一个master.一个node.查看node节点是主机名 # 安装顺序:先在test1 上安装完必要组件后,就开始在 test2 上单独安装node组件,实现node功能,再返回来配置test1加入集 ...

  4. 网络实验 03-交换机划分VLAN配置

    交换机划分VLAN配置 一.实验目标 理解虚拟 LAN(VLAN)基本原理 掌握一般交换机按端口划分 VLAN的配置方法 掌握Tag VLAN配置方法 二.实验背景 某一公司内财务部.销售部的PC通过 ...

  5. Python爬虫学习==>第六章:爬虫的基本原理

    学习目的: 掌握爬虫相关的基本概念 正式步骤 Step1:什么是爬虫 请求网站并提取数据的自动化程序 Step2:爬虫的基本流程 Step3:Request和Response 1.request 2. ...

  6. LinuxC/C++基础——引用

    1.引用(Reference) 1.1引用的基本语法 引用是C++对C的重要扩充,也存在与其他一些编程语言中,并不是C++的发明.通过引用,C++增加了 另外一种给函数传递地址的途径,这就是按引用传递 ...

  7. Pytorch1.0深度学习:损失函数、优化器、常见激活函数、批归一化详解

    不用相当的独立功夫,不论在哪个严重的问题上都不能找出真理:谁怕用功夫,谁就无法找到真理. —— 列宁 本文主要介绍损失函数.优化器.反向传播.链式求导法则.激活函数.批归一化. 1 经典损失函数 1. ...

  8. finereport 填报 单元格 JS 触发 提交SQL 事件

    var location = this.options.location; var cr = FR.cellStr2ColumnRow(location); var col = cr.col; var ...

  9. 【Linux开发】内核模块简介

    一. 摘要 这篇文章主要介绍了Linux内核模块的相关概念,以及简单的模块开发过程.主要从模块开发中的常用指令.内核模块程序的结构.模块使用计数以及模块的编译等角度对内核模块进行介绍.在Linux系统 ...

  10. Angular build编译内存溢出"JavaScript heap out of memory"的解决办法

    关于最近使用angular build编译打包的时候,遇到内存溢出的突发情况,做一个简单记录 编译报错如下↓↓↓ 报错信息很直观地指出是内存溢出了.是什么导致了内存溢出呢?其根本原因在于 nodejs ...