认识内存管理
不管什么样的编程语言,在代码的执行过程中都是需要给它分配内存的,不同的是某些编程语言需要我们自己手动的管理内存(如:比如C、C++都是需要手动来管理内存的申请和释放的(malloc和free函数)),某些编程语言会可以自动帮助我们管理内存(如:JavaScript通常情况下不需要手动管理)
内存管理的生命周期
- 申请内存
- 使用内存
- 释放内存
JS的内存分配
JavaScript会在定义变量时为我们分配内存
而对于变量,它有两种类型:简单数据类型(内存直接存的值),分别是Undefined,Null,Boolean,Number,String,Symbol,还有一种复杂数据类型(内存存的是引用地址):Object(对象、数组、函数)。
这两种类型在内存管理中分配内存的方式是不一样的:
- 简单数据类型(基本类型)的值直接在栈空间进行分配,值与值之间是独立存在,修改一个变量的值不会影响其他变量的值
- 复杂数据类型(引用类型)存在堆内存中,每创建一个对象,就会在堆内存中开辟出一个新的空间,而变量保存的对象的内存地址(对象的引用,如果两个变量保存的同一个对象,当一个变量改变属性值时,另一个变量的属性值也会变 )
简单数据类型的内存分配
// 定义变量
var a = 1;
var b = 2;
复杂数据类型的内存分配
// 定义变量
const a = 1;
const b = 2;
const info {
name: 'eureka',
age: 100
}
function foo() {
var c = 3;
var d = 4;
console.log(a+b);
}
JS的内存释放
因为内存的大小是有限的,所以当内存不再需要的时候,我们需要对其进行释放,以便腾出更多的内存空间。
因为JS不用手动去释放内存,它有自己的垃圾回收机制(Garbage Collection,简称:GC),那JavaScript是怎么判断这个内存不再使用,进而被当成垃圾回收呢?
- 当对象不再被引用时,JavaScript就认为它不再使用 (通过引用计数来确认)
- 对象不能从根上访问到时,JavaScript也会把它认定为是垃圾 (通过标记清除来确认)
常见的GC算法——引用计数
当一个对象有一个引用指向它时,那么这个对象的引用就+1,当一个对象的引用为0时,这个对象就可以被销毁掉;
使用引用计数会有一个很严重的问题:循环引用。
循环引用指的是对象A中包含一个指向对象B的指针,而对象B中也包含一个指向对象A的引用,并且不能通过代码访问它们,这样它们的引用永远不会为0,垃圾回收机制不能将这两个对象释放,进而会造成内存泄露
let a = {
name: 'a',
dad: b
};
let b = {
name: 'b',
son: a
}
a = null;
b = null;
常见的GC算法——标记清除
- 这个算法是假定一个根对象(root object),垃圾回收器会定期从这个根开始,找所有从根开始有引用到的对象,对于哪些没有引用到的对象,就认为是不可用的对象;
- 根对象在浏览器中是 window 对象,在 NodeJS 中是 global 对象,只有当程序退出或者关闭网页或者关闭浏览器的时候才会销毁。
- 这个算法可以很好的解决循环引用的问题;