像c语言这样的底层语言一般都有底层的内存管理接口,如malloc()和free()。相反,JavaScript是在创建变量(对象,字符串...)时自动进行了内存分配,并且在不使用它们时自动释放,释放的过程称为垃圾回收。
内存生命周期
不管什么程序语言,内存生命周期基本是一致的:
- 分配你所需要的内存
- 使用分配到的内存(读、写)
- 不需要时将其释放\归还
所有语言第二部分都是明确的。第一和第三部分在底层语言中是明确的,但在像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(); // 分配一个 Date 对象
var e = document.createElement('div'); // 分配一个 DOM 元素(对象)
有些方法分配新变量或者新对象
var s = "azerty";
var s2 = s.substr(0, 3); // s2 是一个新的字符串
// 因为字符串是不变量,
// JavaScript 可能决定不分配内存,
// 只是存储了 [0-3] 的范围。
var a = ["ouais ouais", "nan nan"];
var a2 = ["generation", "nan nan"];
var a3 = a.concat(a2);
// 新数组有四个元素,是 a 连接 a2 的结果
使用值的过程对内存的操作
使用值的过程实际上是对分配内存进行读取于写入的操作。
- 可能是写入一个变量或者一个对象的属性值
- 甚至传递函数的参数
垃圾回收
标记-清除算法
这个算法把“对象是否不再需要”简化定义为“对象是否可以获得”。
这个算法假定设置一个叫做根(root)的对象(在Javascript里,根是全局对象)。垃圾回收器将定期从根开始,找所有从根开始引用的对象,然后找这些对象引用的对象……从根开始,垃圾回收器将找到所有可以获得的对象和收集所有不能获得的对象。
这个算法比前一个要好,因为“有零引用的对象”总是不可获得的,但是相反却不一定,参考“循环引用”。
从2012年起,所有现代浏览器都使用了标记-清除垃圾回收算法。所有对JavaScript垃圾回收算法的改进都是基于标记-清除算法的改进,并没有改进标记-清除算法本身和它对“对象是否不再需要”的简化定义。
举个例子
var fn = function(){}
document.body.onclick = fn
var fn = null //fn还是存在于heap内存中
var fn = function(){}
//fn声明在stack中,存放着它的引用指向在heap中的function(){} 假设引用编号为111
document.body.onclick = fn
//document声明在stack中,它存在一个指向heap中body对象的引用 > body中有一个指向heap中onclick的引用 > onclick对象中存在一个对function(){}的引用(引用编号111)。
var fn = null //在stack中将引用编号111改为了null
//但function(){}还是存在于heap内存中
网友评论