JavaScript内存管理介绍

简介

低级语言,比如C,有低级的内存管理基元,想malloc(),free()。另一方面,JavaScript的内存基元在变量(对象,字符串等等)创建时分配,然后在他们不再被使用时“自动”释放。后者被称为垃圾回收。这个“自动”是混淆并给JavaScript(和其他高级语言)开发者一个错觉:他们可以不用考虑内存管理。

内存生命周期

不管什么程序语言,内存生命周期基本一致:

1.分配你所需要的内存
2.使用它(读、写)
3.当它不被使用时释放   ps:和“把大象装冰箱“一个意思

第一二部分过程在所有语言中都很清晰。最后一步在低级语言中很清晰,但是在像JavaScript等高级语言中,最后一步不清晰。

JavaScript的内存分配

变量初始化

为了不让程序员为分配费心,JavaScript在定义变量时完成内存分配。

代码如下:

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();
var e = document.createElement('div'); //分配一个DOM元素

有些方法分配新变量或者新对象:

代码如下:

var s = "azerty";
var s2 = s.substr(0, 3); // s2 is a new string
//因为string是不变量,JavaScript可能没有分配内存,但只是存储了0-3的范围。

var a = ["ouais ouais", "nan nan"];
var a2 = ["generation", "nan nan"];
var a3 = a.concat(a2); // 新数组中有连接数组a和数组a2中的四个元素。

值的使用

使用值的过程实际上是对分配内存进行读取与写入的操作,这意味着可以写入一个变量或者一个对象的属性值,甚至传递函数的参数。

当内存不再需要使用时释放

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

高级语言解释器嵌入了“垃圾回收器”,主要工作是跟踪内存的分配和使用,以便当分配的内存不再使用时,自动释放它。这个过程是一个近似的,因为要知道某块内存是否需要是 无法判定的 (无法被某种算法所解决).

垃圾回收

如上文所述自动寻找是否一些内存“不再需要”的问题是无法判定的。因此,垃圾回收实现只能有限制的解决一般问题。本节将解释必要的概念,了解主要的垃圾回收算法和它们的局限性。

引用

垃圾回收算法主要依赖于引用的概念。在内存管理的环境中,一个对象如果有访问另一个对象的权限(隐式或者显式),叫做一个对象引用另一个对象。例如,一个Javascript对象具有对它 原型 的引用(隐式引用)和对它属性的引用(显式引用)。

在这里,“对象”的概念不仅特制Javascript对象,还包括函数作用域(或者全局词法作用域)。

引用计数垃圾收集

这是最简单的垃圾收集算法。此算法把“对象是否不再需要”简化定义为“对象有没有其他对象引用到它”。如果没有引用指向该对象(零引用),对象将被垃圾回收机制回收。

例如

代码如下:

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();
// 两个对象被创建,并互相引用,形成了一个循环
// 他们被调用之后不会离开函数作用域
// 所以他们已经没有用了,可以被回收了
// 然而,引用计数算法考虑到他们互相都有至少一次引用,所以他们不会被回收

实际当中的例子

IE 6, 7 对DOM对象进行引用计数回收。对他们来说,一个常见问题就是内存泄露:

代码如下:

var div = document.createElement("div");
div.onclick = function(){
  doSomething();
};
// div有了一个引用指向事件处理属性onclick
// 事件处理也有一个对div的引用可以在函数作用域中被访问到
// 这个循环引用会导致两个对象都不会被垃圾回收

标记-清除算法

这个算法把“对象是否不再需要”简化定义为“对象是否可以获得”。

这个算法假定设置一个叫做根的对象(在Javascript里,根是全局对象)。定期的,垃圾回收器将从根开始,找所有从根开始引用的对象,然后找这些对象引用的对象……从根开始,垃圾回收器将找到所有可以获得的对象和所有不能获得的对象。

这个算法比前一个要好,因为“有零引用的对象”总是不可获得的,但是相反却不一定,参考“循环引用”。

从2012年起,所有现代浏览器都使用了标记-清除垃圾回收算法。所有对JavaScript垃圾回收算法的改进都是基于标记-清除算法的改进,并没有改进标记-清除算法本身和它对“对象是否不再需要”的简化定义。

循环引用不再是问题了

在上面的示例中,函数调用返回之后,两个对象从全局对象出发无法获取。因此,他们将会被垃圾回收器回收。
第二个示例同样,一旦 div 和其事件处理无法从根获取到,他们将会被垃圾回收器回收。

限制: 对象需要明确的不可获得

尽管这是一个限制,但是很少会被突破,这也就是为什么在现实中很少人会去关心垃圾回收机制。

(0)

相关推荐

  • js变量、作用域及内存详解

    基本类型值有:undefined,NUll,Boolean,Number和String,这些类型分别在内存中占有固定的大小空间,他们的值保存在栈空间,我们通过按值来访问的. (1)值类型:数值.布尔值.null.undefined. (2)引用类型:对象.数组.函数. 如果赋值的是引用类型的值,则必须在堆内存中为这个值分配空间.由于这种值的大小不固定(对象有很多属性和方法),因此不能把他们保存到栈内存中.但内存地址大小是固定的,因此可以将内存地址保存在栈内存中. <script type="

  • 跟我学习JScript的Bug与内存管理

    1.JScript的Bug IE的ECMAScript实现JScript严重混淆了命名函数表达式,搞得现很多人都出来反对命名函数表达式,而且即便是现在还一直在用的一版(IE8中使用的5.8版)仍然存在下列问题. 下面我们就来看看IE在实现中究竟犯了那些错误,俗话说知已知彼,才能百战不殆.我们来看看如下几个例子: 例1:函数表达式的标示符泄露到外部作用域 var f = function g(){}; typeof g; // "function" 前面我们说过,命名函数表达式的标示符在

  • js内存泄露的几种情况详细探讨

    内存泄露是指一块被分配的内存既不能使用,又不能回收,直到浏览器进程结束.在C++中,因为是手动管理内存,内存泄露是经常出现的事情.而现在流行的C#和Java等语言采用了自动垃圾回收方法管理内存,正常使用的情况下几乎不会发生内存泄露.浏览器中也是采用自动垃圾回收方法管理内存,但由于浏览器垃圾回收方法有bug,会产生内存泄露. 1.当页面中元素被移除或替换时,若元素绑定的事件仍没被移除,在IE中不会作出恰当处理,此时要先手工移除事件,不然会存在内存泄露. 复制代码 代码如下: <div id="

  • javascript内存分配原理实例分析

    本文实例讲述了javascript内存分配原理.分享给大家供大家参考,具体如下: JavaScript中的变量分为两种,原始值和引用值.原始值指的是原始数据类型的值,比如undefined,null,number,string,boolean类型所表示的值.引用值指的是复合数据类型的值,即Object,Function,Array等. 原始值和引用值存储在内存中的位置分别为栈和堆.原始值是存储在栈中的简单数据段,他们的值直接存储在变量访问的位置.引用值是存储在堆中的对象. 存储在栈中的值是一个指

  • JavaScript 变量、作用域及内存

    JS变量是松散型的(不强制类型)本质,决定了它只是在特定时间用于保存特定值的一个名字而已; 由于不存在定义某个变量必须要保存何种数据类型值的规则,变量的值及其数据类型可以在脚本的生命周期内改变; 一 变量及作用域 1.基本类型和引用类型 // JS变量包含两种不同的数据类型的值:基本类型值和引用类型值; // 1.基本类型值:保存在栈内存中的简单数据段;即这种值完全保存在内存中的一个位置; // 基本类型值包含:Undefined|Null|Boolean|Number|String; // 这

  • JavaScript的内存释放问题详解

    本文详细的讲解了JavaScript及IE浏览器对内存的管理和释放的时机和方法,希望对前端开发人员有所帮助. 一个内存释放的实例 复制代码 代码如下: <SCRIPT LANGUAGE="JavaScript"> <!-- strTest = "1"; for ( var i = 0; i < 25; i ++ ) { strTest += strTest; } alert(strTest); delete strTest; CollectG

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

    你可能听说过JAVA..NET.PHP这些语言有垃圾回收的内存管理机制,但是很少会听到JavaScript也有自己的内存管理机制,JavaScript同样有着类似的垃圾回收功能.本文主要讲述了JavaScript的垃圾回收原理和具体的过程. 简介在底层语言中,比如C,有专门的内存管理机制,比如malloc() 和 free().而Javascript是有垃圾回收(garbage collection)机制的,也就是说JS解释器会自动分配和回收内存.这样就有人觉得,我用的是高级语言,就不用关心内存

  • JavaScript也谈内存优化

    相对C/C++ 而言,我们所用的JavaScript 在内存这一方面的处理已经让我们在开发中更注重业务逻辑的编写.但是随着业务的不断复杂化,单页面应用.移动HTML5 应用和Node.js 程序等等的发展,JavaScript 中的内存问题所导致的卡顿.内存溢出等现象也变得不再陌生. 这篇文章将从JavaScript 的语言层面进行内存的使用和优化的探讨.从大家熟悉或略有耳闻的方面,到大家大多数时候不会注意到的地方,我们一一进行剖析. 1. 语言层面的内存管理 1.1 作用域 作用域(scope

  • 理解Javascript_01_理解内存分配原理分析

    原始值和引用值 在ECMAScript中,变量可以存放两种类型的值,即原始值和引用值. 原始值指的就是代表原始数据类型(基本数据类型)的值,即Undefined,Null,Number,String,Boolean类型所表示的值. 引用值指的就是复合数据类型的值,即Object,Function,Array,以及自定义对象,等等 栈和堆 与原始值与引用值对应存在两种结构的内存即栈和堆 栈是一种后进先出的数据结构,在javascript中可以通过Array来模拟栈的行为 复制代码 代码如下: va

  • 深入理解JavaScript程序中内存泄漏

    垃圾回收解放了我们,它让我们可将精力集中在应用程序逻辑(而不是内存管理)上.但是,垃圾收集并不神奇.了解它的工作原理,以及如何使它保留本应在很久以前释放的内存,就可以实现更快更可靠的应用程序.在本文中,学习一种定位 JavaScript 应用程序中内存泄漏的系统方法.几种常见的泄漏模式,以及解决这些泄漏的适当方法. 一.简介 当处理 JavaScript 这样的脚本语言时,很容易忘记每个对象.类.字符串.数字和方法都需要分配和保留内存.语言和运行时的垃圾回收器隐藏了内存分配和释放的具体细节. 许

  • JavaScript 事件对内存和性能的影响

    虽说事件处理程序可以为现代 Web 页面添加很强的交互能力,但是不分青红皂白就添加大量的事件处理程序绝对是一种愚蠢的行为. 我们来分析一下:事件处理程序本质上是一种函数,是一种对象,存放在内存中,设置大量的事件处理程序会使内存中的对象变多,Web 程序的性能会变得越来越差,用户体验很不好. 为了更好地利用好事件处理程序,便出现了事件委托,用来提升性能. 事件委托 事件委托(event delegation):把若干个子节点上的相同事件的处理函数绑定到它的父节点上去,在父节点上统一处理从子节点冒泡

  • js 内存释放问题

    CollectGarbage(); setTimeout("CollectGarbage();", 1); 这里之所以使用setTimeout(),因为可以彻底回收当前所有对象,防止变量之间的引用导致释放失败,可以当作一个保障措施,按照道理来说,这里不会执行了. 使用的时候需要注意,一定在所有函数执行完毕之后执行,否则,setTimeout()和setIntervalue()等东东都将无法正常工作了. 另外,切忌胡乱使用,一般来说,顶级对象致空"var xxx='ss';xx

随机推荐