Javascript内存管理

什么是内存

内存(Memory)也被称为内存储器和主存储器,其做用是用于暂时存放CPU中的运算数据,以及与硬盘等外部存储器交换的数据。计算机中全部程序的运行都是在内存中进行的,所以内存的性能对计算机的影响很是大。javascript

内存生命周期

不管什么语言,内存周期基本是一致的:html

  1. 分配你所须要的内存
  2. 使用分配到的内存(读、写)
  3. 不须要时将其释放\归还

JavaScript的内存分配

值的初始化java

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';
}, false);

 经过函数调用分配内存算法

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

内存使用

使用值的过程其实是对分配内存进行读取与写入的操做。读取与写入多是写入一个变量或者一个对象的属性值,甚至传递函数的参数。数组

内存释放

大多数内存管理的问题都在这个阶段。在这里最艰难的任务是找到“所分配的内存确实已经再也不须要了”。它每每要求开发人员来肯定在程序中哪一块内存再也不须要而且释放它。浏览器

垃圾回收

引用计数

这是最简单的垃圾收集器算法。若是没有引用指向这个对象的时候,这个对象就被认为是“能够做为垃圾收集”。闭包

var o = { 
 a: {
   b:2
 }
}; 
// 两个对象被建立,一个做为另外一个的属性被引用,另外一个被分配给变量o
// 很显然,没有一个能够被垃圾收集


var o2 = o; // o2变量是第二个对“这个对象”的引用
o = 1;      // 如今,“这个对象”的原始引用o被o2替换了

var oa = o2.a; // 引用“这个对象”的a属性
// 如今,“这个对象”有两个引用了,一个是o2,一个是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();

标记清除

JavaScript 中最经常使用的垃圾收集方式是标记清除(mark-and-sweep)。当变量进入环境(例如,在函数中声明一个变量)时,就将这个变量标记为“进入环境”。从逻辑上讲,永远不能释放进入环境的变量所占用的内存,由于只要执行流进入相应的环境,就可能会用到它们。而当变量离开环境时,则将其标记为“离开环境”。这个算法由如下步骤组成:spa

  1. 这个垃圾收集器构建一个“roots”列表。Root是全局变量,被代码中的引用所保存。在 JavaScript中,“window”就是这样的做为root的全局变量的例子。
  2. 全部的root都会被监测而且被标志成活跃的(好比不是垃圾)。全部的子代也会递归地被监测。全部可以由root访问的一切都不会被认为是垃圾。
  3. 全部再也不被标志成活跃的内存块都被认为是垃圾。这个收集器如今就能够释放这些内存并将它们返还给操做系统。 

内存泄漏

内存泄漏能够被定义为应用程序再也不须要的内存,可是因为某些缘由不会返回到操做系统或可用内存池。

常见的JavaScript泄露

1. 全局变量

一个未声明变量的引用会在全局对象内部产生一个新的变量。在浏览器的状况,这个全局变量就会是window。

function foo(arg) {
   bar = "some text";
}
等同于:

function foo(arg) { window.bar = "some text"; }

若是bar被指望仅仅在foo函数做用域内保持对变量的引用,而且你忘记使用var去声明它,一个意想不到的全局变量就产生了。

2.被遗忘的计时器和回调

setInterval 在 JavaScript 中是常常被使用的。大多数提供观察者和其余模式的回调函数库都会在调用本身的实例变得没法访问以后对其任何引用也设置为不可访问。 可是在setInterval的状况下,这样的代码很常见:




renderer所表明的对象在将来可能被移除,让部分interval 处理器中代码变得再也不被须要。然而,这个处理器不可以被收集由于interval依然活跃的(这个interval须要被中止从而表面这种状况)。
若是这个interval处理器不可以被收集,那么它的依赖也不可以被收集。这意味这存储大量数据的severData也不可以被收集。

3. 闭包
闭包的特性是内部函数可以访问外部函数的做用域。
var serverData = loadData(); setInterval(function() { var renderer = document.getElementById('renderer'); if(renderer) { renderer.innerHTML = JSON.stringify(serverData); } }, 5000); //每5000ms执行一次
var sayName = function(){
  var name = 'jozo';
  return function(){
    alert(name);
  }
};
var say = sayName(); 
say();

sayName返回了一个匿名函数,该函数又引用了sayName的局部变量name,sayName 调用后变量name应该被回收,可是因为say继续引用,致使没法回收。

 

 

原文出处:https://www.cnblogs.com/hutuzhu/p/11101437.html

相关文章
相关标签/搜索