likes
comments
collection
share

04 JavaScript内存管理与闭包

作者站长头像
站长
· 阅读数 40

什么是内存管理???

不管什么样的语言,在执行过程中都是需要给它分配内存的 ,不同的是某些编程语言 需要我们自己手动的管理内存 ,某些语言会自动帮助我们管理内存

内存的管理都会有如下生命周期:

  1. 第一步:分配申请你需要的内容
  2. 第二步:使用分配的内存
  3. 第三步:不需要使用时,对其进行释放

不同的编程语言对于第一步和第三步会有不同的实现:

  • 手动管理内存:需要手动来申请释放,如: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 空闲时尝试运行,以减少可能对代码执行的影响

04 JavaScript内存管理与闭包

闭包

什么是闭包?

  • 闭包在实现上是一个结构体 ,它存储了一个函数和一个关联的环境
  • 从广义的角度来说:JavaScript中的函数都是闭包
  • 从狭义的角度来说:JavaScript中一个函数,如果访问了外层作用域的变量,那么它是一个闭包

如下一定形成了闭包:

function makeAdder(count){
  return function(num){
    return count+num
  }
}

var add10=makeAdder(10)
console.log(add10(5))

04 JavaScript内存管理与闭包

这个时候makeAdder 函数执行完毕,正常情况下我们的AO对象会被释放,但是因为0xb00 的函数中有作用域引用指向了这个AO对象,所以它不会被释放掉

04 JavaScript内存管理与闭包

闭包的内存泄漏

在上面的案例中,如果后续我们不再使用add10 函数了,那么该函数对象应该要被销毁掉,并且其引用着的父作用域AO也应该被销毁掉,但是目前因为在全局作用域下add10 变量对0xb00 的作用域中AO(0x200) 的引用,所以最终会造成这些内存都是无法释放的

如何解决?

add10 设置为null 时,就不再对函数0xb00 有引用,那么对应的AO对象0x200就不可达了,在GC的下一次检测中它们就会被销毁掉了

转载自:https://juejin.cn/post/7179610770135580709
评论
请登录