面试题汇总(28)

78 阅读2分钟

JS 垃圾回收算法有哪些

function fn() {
  const a = 'aa'
  console.log(a)
  const obj = { x: 100 }
  console.log(obj)
}

fn1()

function fn2() {
  const obj = { x: 100 }
  window.obj = obj // 符合用户预期
}

fn2()

function getDataFns() {
  const data = {}
  return {
    get(key) {
      return data[key]
    }
    set(key, value) {
      data[key] = value
    }
  }
}

const { get, set } = getDataFns()
set('x', 100)
get('x')

  • 引用计数(之前) 每个对象都有一个引用计数器,记录有多少个变量或对象引用了它。当变量赋值为对象时,引用计数 +1。当变量被赋值为其他值(或离开作用域、被设为 null)时,原引用的对象引用计数 -1。当引用计数为 0 时,对象被判定为“可回收”,其内存会被垃圾回收器释放。
let a = { x: 100 }  // 计数: 1
let a1 = a          // 计数: 2
a = 10              // 计数: 1 
a1 = null           // 计数: 0

无法解决循环引用问题

function fn3() {
  const obj1 = {}
  const obj2 = {}
  obj1.a = obj2
  obj2.a = obj1
}

fn3()
  • 标记清除(现代)
    • 标记阶段
      垃圾回收器从根对象(如全局对象window)开始,递归地标记所有从根对象可达的对象,这些对象是活动对象,即仍在使用中的对象。
      那些没有被标记的对象就是不可达的,被认为是垃圾对象,即不再被程序使用的对象。
    • 清除阶段
      垃圾回收器会遍历整个堆内存,将所有未被标记的垃圾对象所占用的内存空间释放掉,回收这些内存以便后续重新分配使用。

JS 闭包是内存泄露么?

在 JavaScript 中,闭包本身不是内存泄漏,但不当使用闭包可能导致内存泄漏。

如何检测 JS 内存泄露

打开浏览器开发者工具(通常是按 F12 或 Ctrl + Shift + I)。 切换到 Memory 面板。 进行一次初始的内存快照(点击 Take snapshot)。 执行你怀疑可能导致内存泄露的操作。 再进行一次内存快照。 对比两次快照,查看哪些对象在操作后没有被回收,并且数量持续增加。

内存泄露的场景

  • 被全局变量、函数引用,组件销毁时未清除
  • 被全局事件、定时器引用,组件销毁时未清除
  • 被自定义事件引用,组件销毁时未清除