优化实战 第 68 期 - 垃圾回收机制:保持你的代码干净高效

235 阅读2分钟

无论是前端还是后端开发,JavaScript 都被广泛应用。作为一名优秀的开发者,深入了解 JavaScript 的垃圾回收机制至关重要,这不仅能提高代码性能,还能有效避免内存泄漏,从而显著提升应用的性能和稳定性。

什么是垃圾回收

垃圾回收是编程语言自动管理内存的一种机制。其主要目的是负责内存的分配和释放,它的核心任务是回收不再使用的对象,以避免内存泄漏。

在 JavaScript 中,垃圾回收是由 JavaScript 引擎自动处理的,也就是说开发者不需要手动管理内存的分配和释放。

垃圾回收机制

  • 引用计数(Reference Counting)

    • 每个对象都有一个引用计数,当对象被引用时,计数加 1,引用解除时,计数减 1

    • 当计数为 0 时,表示该对象不再被引用,可以被回收

    • 缺点:容易导致循环引用问题,即两个或多个对象相互引用,导致计数永远不为 0,无法回收

  • 标记-清除(Mark-and-Sweep)

    • 标记阶段:从根对象(全局对象、当前执行上下文的变量等)出发,递归标记所有可以到达的对象

    • 清除阶段:未被标记的对象被认为是不可达的,可以回收

    • 优点:能有效解决引用计数的循环引用问题

  • 分代回收(Generational Garbage Collection)

    • 基于对象的存活时间优化回收过程,将内存分为 ”新生代“ 和 “老生代” 两部分

    • 新生代:存放新创建的对象,回收频繁,但成本低

    • 老生代:存放存活时间较长的对象,回收频率低,但回收成本较高

    • 优点:提高垃圾回收效率,减少停顿时间

避免内存泄漏

  • 及时解除引用

    // 添加事件监听器
    element.addEventListener('eventName', eventHandler)
    // 在不需要时移除事件监听器
    element.removeEventListener('eventName', eventHandler)
    

    不再使用的对象及时解除引用(如移除事件监听器、清空引用变量)

  • 避免全局变量

    const example = () => {
        let localVar = 'This is a local variable'
    }
    

    全局变量会一直存在,尽量使用局部变量或块作用域(如 let 和 const)

  • 谨慎使用闭包

    function createClosure() {
        let largeObject = { /* large object */ }
        return function() {
            console.log(largeObject)
        }
    }
    let closure = createClosure()
    closure = null // 解除引用
    

    闭包会持有对外部作用域变量的引用,注意合理使用和解除不再需要的引用