翻译:javascript 内存管理

原文连接:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Memory_Management?redirectlocale=en-US&redirectslug=JavaScript%2FMemory_Managementjavascript

javascript 内存管理 

介绍

低级语言(c语言),拥有底层的内存管理原语:malloc() 和 free()。而在 javascript 中,变量的内存是在建立过程当中自动分配的,当变量再也不被引用时会自动释放其内存。后面的这一处理过程被称为“垃圾回收”。这里的“自动”致使了一系列的困惑,而且给 javascript (高级语言) 的开发者带来一种错误的印象,即他们不须要关心内存的管理。

内存的生命周期

抛开编程语言来讲,几乎全部的内存生命周期都具备相同的特色:
1. 按需分配内存
2. 读,写
3. 再也不被引用时,释放
前两个特色在全部的语言中都是显而易见的,第三点在低级编程语言中也很容易可以办到,可是在大部分高级语言中(好比 javascript)则很复杂。

javascript 中内存的分配

  • 变量的初始化

为了方便开发者不去关心内存分配的事情,javascript 在变量申明的时候就已经顺便分配了内存。java

var n = 123; // 给一个number分配内存
var s = "azerty"; // 给一个string分配内存
var o = {
  a: 1,
  b: null
}; // 给一个object分配内存

var a = [1, null, "abra"]; // (和object同样)给一个array及里面的值分配内存

function f(a){
  return a + 2;
} // 给一个function分配内存(function也能够看成一个object)

// 函数的表达式一样也是至关于给一个object分配内存
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 是一个新的string
// 因为strings是一些不可变的值组成的,javascript可能不会给其分配内存,而只是存储一个[0,3]的范围

var a = ["ouais ouais", "nan nan"];
var a2 = ["generation", "nan nan"];
var a3 = a.concat(a2); // 新的array,包含四个元素,分别由a和a2的元素组成
  • 变量的使用
使用一个变量基本上也就意味着内存的读和写,这个过程能够经过读或写变量的值或一个对象的属性值或者甚至是给函数传递参数完成。
  • 内存再也不被引用时,释放
内存管理中遇到的大多数问题都会集中在这个阶段。这里最难的任务的如何判断一个已分配的内存再也不继续被使用。一般这个问题是由开发者决定这段内存将再也不被使用,而后释放它。
通常高级语言的编译器会内嵌一小段程序,它被称做“垃圾回收者",这个垃圾回收者的工做顾名思义,固然是回收垃圾的嘛,这里的"垃圾"指的就是将再也不被使用的内存片断。这个过程也只是个近似的处理,由于目前为止,判断某个片断的内存是否正在被引用这个问题是不可断定的,也就是说不能在多项式时间内求解。
 

垃圾回收

上面已经提到判断一段内存是否再也不被引用是图灵不可断定问题。所以垃圾回收机制也就采用了一个限制性的方法来解决这个问题。这部分先会介绍一些必要的概念以便理解垃圾回收的算法及其限制的地方。
  • 引用

垃圾回收算法的主要思想主要依赖的一个概念就是引用。在一段内存管理的上下文中,若是一个对象访问了另外一个对象(不管是显式的仍是隐式的),咱们就说前者引用了后者。例如,一个 javascript 对象能够访问它的 prototype 属性(隐式的), 也能够调用它本身的属性值(显式的)。
在这个上下文中,"object" 的概念被扩展了,它指代的是不只是传统的 object 对象,同时还包含了函数域(或全局词法做用域)。
  • 基于引用计数的垃圾回收

这是最基本的垃圾回收算法。这个算法把问题"一个对象再也不被引用"简化成"一个对象没有被其余对象引用"。若是一个对象上的引用计数为0,则代表这个对象是能够被看成垃圾回收的。
 
示例:
var o = { 
  a: {
    b:2
  }
}; // 建立了两个对象,一个引用了另一个对象做为其属性 显然,都不能够被回收


var o2 = o; // o2 也引用了 o
o = 1; // 如今,o中原始的对象有一个惟一的引用 o2的变量中 

var oa = o2.a; // 引用o2的a属性
// 这个对象产生了两个引用,一个是o2的a对象,另外一个一个是oa变量

o2 = "yo"; // 原始的o对象如今是0引用
// o能够被回收
// 然而其属性a仍然被oa引用了,所以其内存不能被释放

oa = null; // 原始对象o中的a是0引用了
// 此时能够被回收
 
缺点:循环引用
这个基本算法有一个缺点,一个对象A引用了另外一对象B,对象B又反过来引用了A。这样A,B之间就造成了一个循环。A,B可能都不会再引用,可是根据上面的算法A和B均不会被回收。
function f(){
  var o = {};
  var o2 = {};
  o.a = o2; // o 引用了 o2
  o2.a = o; // o2 引用了 o

  return "azerty";
}

f();
 
现实生活中的例子
IE的6,7中针对 dom 对象使用的就是基于引用计数的垃圾回收机制。因此,在IE6,7中,很容易产生系统的内存泄露。
var div = document.createElement("div");
div.onclick = function(){
  doSomething();
}; // div 经过 事件引用了其onclick属性
// handler一样引用了div
// 循环引用,内存泄露
  • 标记-删选 算法

这个算法将问题"一个对象再也不被引用"简化成"一个对象是没法访问的"。
这个算法假设知道一组对象的根(在 javascript 中这个根就是全局变量 window),这个垃圾回收者会不间断的自根节点遍历其全部子节点。这样,垃圾回收者最后会找出全部可访问的对象及全部不可访问的对象。
这个算法好于以前的算法,由于“一个引用计数为0的对象”也是一个不可访问的对象。而反过来却不成立,好比循环引用。
截至2012年,全部现代浏览器都内置了一个标记和回收垃圾的收集器。在过去的几年中,javascript的垃圾回收领域(代/增量/并发/并行垃圾收集)中的全部改进都是在实现这种算法的改进而不是改善垃圾收集算法自己也不是在改进问题描述的简化模型上。
 
循环引用再也不是一个问题
上面第一个示例中,函数调用以后,这两个对象将再也不被引用,其余的变量直接可从全局变量中访问。所以, 它们将被垃圾回收者认为是不可访问的。
第二个示例也是如此,一旦这个 div 及其 handler不可被顶级 roots 访问,它们都将被回收掉,即便它们之间会产生互相引用。
 
缺点:对象须要显式的指明不可访问
虽然这被标记为一个缺点,其实这在现实中很难出现,由于也不会有不少人常常关注垃圾的回收。

更多

相关文章
相关标签/搜索