Web前端之路

JavaScript内存管理

2017-05-11  本文已影响75人  糖小工

内存生命周期

不管什么程序语言,内存生命周期基本是一致的:
1.分配你所需要的内存
2.使用分配到的内存(读、写)
3.不需要时将其释放\归还

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);

原始值和引用值

在ECMAScript中,变量可以存放两种类型的值,即原始值和引用值。
原始值:Undefined、Null、Number、String、Boolean、Symbol
引用值:Object、Function、Array...

栈和堆

与原始值和引用值对应的两种结构即的内存栈和堆
栈是一种后进先出的数据结构,在JavaScript的可以通过Array来模拟栈的行为

var arr = []; //创建一个栈
arr.push("apple");//压入元素"apple" ["apple"]
arr.push("orange");//压入元素"orange"   ["apple","orange"]
arr.pop();//弹出"orange"      ["apple"]
arr.push("banana");//压入元素"banana"   ["apple","banana"]

与之对应的内存图:


栈的示意图

原始值是存储在栈中的简单数据段,他们的值直接存储在变量访问的位置。

堆是存放数据的基于散列算法的数据结构,用以表示一个内存中大的未被组织的区域,在JavaScript中,引用值被分配在一个堆中,

引用值是存储在堆中的对象,也就是,存储在变量出的值(及值对象的变量,存储在栈中)是一个指针,指向存储在堆中的实际对象。

例:var obj = new Object(); obj存储在栈中它指向于new Object()这个对象,而new Object()是存放在堆中的。

那为什么引用值要放在堆中,而原始值要放在栈中,不都是在内存中吗,为什么不放在一起呢?那接下来,让我们来探索问题的答案!

首先,看一下代码:

function Person(id,name,age){
    this.id = id;
    this.name = name;
    this.age = age;
}
 
var num = 10;
var bol = true;
var str = "abc";
var obj = new Object();
var arr = ['a','b','c'];
var person = new Person(100,"笨蛋的座右铭",25);

然后来看一下内存分析图

堆栈内存分配图

变量num,bol,str为基本数据类型,它们的值,直接存放在栈中,obj,person,arr为复合数据类型,他们的引用变量存储在栈中,指向于存储在堆中的实际对象。

由上图可知,我们无法直接操纵堆中的数据,也就是说我们无法直接操纵对象,但我们可以通过栈中对对象的引用来操作对象

现在让我们来回答为什么引用值要放在堆中,而原始值要放在栈中的问题:

记住一句话:能量是守衡的,无非是时间换空间,空间换时间的问题

堆比栈大,栈比堆的运算速度快,对象是一个复杂的结构,并且可以自由扩展,如:数组可以无限扩充,对象可以自由添加属性。将他们放在堆中是为了不影响栈的效率。而是通过引用的方式查找到堆中的实际对象再进行操作。相对于简单数据类型而言,简单数据类型就比较稳定,并且它只占据很小的内存。不将简单数据类型放在堆是因为通过引用到堆中查找实际对象是要花费时间的,而这个综合成本远大于直接从栈中取得实际值的成本。所以简单数据类型的值直接存放在栈中。

值的使用

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

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

大多数内存管理的问题都在这个阶段。在这里最艰难的任务是找到“所分配的内存确实已经不再需要了”。它往往要求开发人员来确定在程序中哪一块内存不再需要并且释放它。
高级语言解释器嵌入了“垃圾回收器”,它的主要工作是跟踪内存的分配和使用,以便当分配的内存不再使用时,自动释放它。这只能是一个近似的过程,因为要知道是否仍然需要某块内存是无法判定的 (无法通过某种算法解决).

垃圾回收

如上文所述自动寻找是否一些内存“不再需要”的问题是无法判定的。因此,垃圾回收实现只能有限制的解决一般问题

上一篇下一篇

猜你喜欢

热点阅读