内存管理以及GC算法
- 内存管理
-
- JavaScript的内存管理
- 什么是垃圾
- GC回收算法
-
- 引用计数算法
- 标记清除算法
- 标记整理算法
内存管理
内存管理是开发者申请,使用,释放内存的过程,但JavaScript的内存管理是自动的,它内部并没有暴露api给开发者去操作内存。
JavaScript的内存管理
JavaScript并没有直接暴露api给开发者去申请,使用以及释放内存,它内部会自动分配内存
申请内存
// javascript 在声明变量的时候会自动分配一定的内存空间
let a = []
使用内存
// 使用内存实际就是对变量进行读写操作
let a = {}
// 写操作
a.name = 'xiaoming'
// 读操作
console.log(a.name)
释放内存
// JavaScript 中会自动回收垃圾变量,当对象不再被引用或者不是可达对象就被视为垃圾
let a = {}
a = null
// 此时 {} 没有被引用,就会被JavaScript内部的机制自动回收
什么是垃圾
上面说到JavaScript会自动回收垃圾变量,那么什么是垃圾呢?
对象不再被引用
我们都知道,
let a = {}
实际是a变量指向
{}
的地址,当
a
赋值为空的时候,
a
不在指向
{}
,此时没有变量指向
{}
的内存空间地址,
{}
就是垃圾
// a引用{}
let a = {}
// b = a,因此b和a都指向{},所以b也引用了{}
let b = a
// a不再引用{},但b还再引用{},所以{}不是垃圾
a = null
// b也不再引用{},{}是垃圾
b = null
对象不能从根上访问
这里的根指的是全局,对象如果能在全局通过引用或者作用域链被访问到,则视为可达对象,否则视为垃圾
function test() {
let a = {}
}
test()
// 函数test执行完毕后,a变量无法在全局被访问到,因此a是垃圾,被回收
function get() {
let b = {}
return b
}
let _b = get()
// 函数get执行完毕后,b变量被全局下的_b接收,在全局我们可以通过_b访问到b变量,因此b不是垃圾,不会被回收
GC回收算法
引用计数算法
引用计数算法,会记录对象被引用的次数,当引用次数为0的时候,就会立即被回收
let a = {} // 计数1
let b = {}
b.obj = a // 计数2
a = null // 计数1
b.obj = null // 计数0 回收
优缺点
- 可以即时回收垃圾对象
- 减少程序卡顿时间
- 无法回收循环引用对象
- 消耗资源较大
疑问
- 对于循环引用对象无法被回收的概念还是不太了解
- 还有函数内部的对象被引用会不会计数
标记清除算法
标记清除算法的原理是:
- 循环全局下所有对象,并标记可达对象
- 循环全局下所有对象,并释放没有被标记的对象
- 将释放的空间放入空闲列表
当再次申请内存的时候可以直接在空闲列表申请,优化申请的速度,但由于在申请空间的时候,空间是不连续的,所以空闲列表的空间内存是碎片化的,在某些情况下不能被很好的利用
优缺点
- 可以回收循环引用的对象
- 容易产生碎片化空间,不利于再次利用,造成浪费
- 不会立即回收垃圾对象
标记整理算法
标记清除算法的原理是:
- 循环全局下所有对象,并标记可达对象
- 循环全局下所有对象,并释放没有被标记的对象
- 将释放的空间放入空闲列表,并整理空闲列表中的碎片化空间,使其最大程度的连续放置
标记整理实际是标记清除的优化版本,优化了碎片空间的难以利用性
优缺点
- 可以回收循环引用的对象
- 不会立即回收垃圾对象