04 JavaScript内存管理与闭包
什么是内存管理???
不管什么样的语言,在
执行过程中都是需要给它分配内存的
,不同的是某些编程语言
需要我们自己手动的管理内存
,某些语言会自动帮助我们管理内存
内存的管理都会有如下生命周期:
- 第一步:
分配申请你需要的内容
- 第二步:
使用分配的内存
- 第三步:
不需要使用时,对其进行释放
不同的编程语言对于第一步和第三步会有不同的实现:
手动管理内存
:需要手动来申请
和释放
,如:c语言自动管理内存
:自动帮助我们管理内存,如:Java,JavaScript,Python
JavaScript会在定义数据
时为我们分配内存,为原始数据类型
在栈空间中进行分配,为复杂数据类型
在堆内存中进行分配,并且将这块空间的指针返回值变量引用
JavaScript的垃圾回收
手动垃圾回收的特点:管理方式非常低效,影响编写逻辑的代码的效率,对开发者要求很高,一不小心就会产生内存泄漏
大部分现代编程语言
都是有自己的垃圾回收机制:
- 垃圾回收机制的英文
Garbage Collection
简称GC
- 对于
那些不再使用的对象
,我们都称之为是垃圾
,需要被回收,以释放更多的内存空间 - 而我们的语言运行环境都会存在
垃圾回收器(GC)
常见的GC算法-引用计数(Reference counting)
当一个对象有一个引用指向它时
,这个对象的引用就+1,当引用为0
时,这个对象就可以被销毁掉
弊端: 会产生循环引用
常见的GC算法-标记清除(mark-Sweep)
标记清除的核心思路是可达性(Reachability)
,这个算法是设置一个根对象(root object)
,垃圾回收器会定期从这个根
开始,找所有从根
开始有引用到的对象
,对于那些没有引用到的对象,就认为是不可用的对象
,这个算法可以很好解决循环引用问题。
常见的GC算法-其他算法优化补充
JS引擎比较广泛的采用的就是可达性中的标记清除
算法,当然类似于V8引擎为了进行更好的优化
,它在算法的实现细节上也会结合一些其他的算法
- 标记整理(Mark-Compact): 和标记清除类似,不同的是,回收期间同时会将保留的存储对象
搬运汇集到连续的内存空间
,从而整合空闲空间
,避免内存碎片化
- 分代收集(Generational collection):对象被分成两组:
新的
和旧的
,许多对象出现,完成它们的工作并很快死去,它们可以很快被清除
,那些长期存活的对象会变得老旧
,而且被检查的频次也会减少 - 增量收集(Incremental collection):如果有许多对象,并且我们试图
一次遍历并标记整个对象集,则可能需要一些时间,并在执行过程中带来明显的延迟
,所以引擎试图将垃圾收集工作分成几部分做
,然后将这几部分逐一处理,这样会有许多微小的延迟而不是一个大的延迟
- 闲时收集(Idle-time collection):垃圾回收器只会在
CPU
空闲时尝试运行,以减少可能对代码执行的影响
闭包
什么是闭包?
- 闭包在实现上是一个
结构体
,它存储了一个函数和一个关联的环境
- 从广义的角度来说:JavaScript中的函数都是闭包
- 从狭义的角度来说:JavaScript中一个函数,如果访问了外层作用域的变量,那么它是一个闭包
如下一定形成了闭包:
function makeAdder(count){
return function(num){
return count+num
}
}
var add10=makeAdder(10)
console.log(add10(5))
这个时候makeAdder
函数执行完毕,正常情况下我们的AO对象会被释放,但是因为0xb00
的函数中有作用域引用指向了这个AO对象,所以它不会被释放掉
闭包的内存泄漏
在上面的案例中,如果后续我们不再使用add10
函数了,那么该函数对象应该要被销毁掉,并且其引用着的父作用域AO也应该被销毁掉,但是目前因为在全局作用域下add10
变量对0xb00
的作用域中AO(0x200)
的引用,所以最终会造成这些内存都是无法释放的
如何解决?
当add10
设置为null
时,就不再对函数0xb00
有引用,那么对应的AO对象0x200就不可达了,在GC的下一次检测中它们就会被销毁掉了
转载自:https://juejin.cn/post/7179610770135580709