Javascript-浏览器垃圾回收机制、哪些操作会有内存泄漏的风险?

72 阅读3分钟

一、垃圾回收核心

1. 两种回收策略

策略原理缺点
标记清除从根对象出发标记可达对象,清除未标记的全堆遍历性能消耗
引用计数统计引用次数,次数为0时回收循环引用无法回收

循环引用示例

function leak() {
  let obj1 = {a: obj2}; // ←┐
  let obj2 = {a: obj1}; // ─┘
}

2. 避免回收性能问题

  • 数组清空用 arr.length = 0 而非 arr = []
  • 不再用的对象设为 null
  • 复用函数避免重复创建

二、内存泄漏四大场景

场景示例代码解决方法
意外全局变量function() { leakVar = '未声明变量' }使用严格模式 'use strict'
未清除定时器setInterval(() => {...}, 1000) 未调用 clearInterval组件卸载时清理定时器
残留DOM引用const dom = document.getElementById('del'); dom.parentNode.removeChild(dom) 但保留 dom 变量删除后置 dom = null
闭包不当使用function outer() { let bigData = ...; return function() { /* 未用bigData */ }}及时解除闭包引用

三、高频面试题

  1. 如何检测内存泄漏?
    → Chrome DevTools的Memory面板:

    • 拍快照对比内存增长
    • 使用Performance录制查看内存曲线
  2. 闭包一定会导致内存泄漏吗?
    → 不会!只有闭包中保留不再需要的大对象才会泄漏
    → 正确做法:使用后解除引用 fn = null

  3. WeakMap/WeakSet 如何避免内存泄漏?
    → 键是弱引用,不影响垃圾回收

    const wm = new WeakMap();
    let obj = {};
    wm.set(obj, 'data'); 
    obj = null; // WeakMap中的条目自动清除
    

四、记忆口诀

"标记清除找可达,引用计数循环卡
泄漏四兄弟:全局、定时、DOM、闭包
严格模式加清理,弱引用解难题"

回答:

首先浏览器的垃圾回收机制是指js代码在运行时,需要分配内存空间来储存变量和值。当变量不再参与运行时,就需要从系统中收回被占用的内存空间,这就是垃圾回收机制。

常用的回收策略有两个

一个是引用计数策略,统计对象的引用次数,次数为零时进行回收,但此时就会出现一个漏洞就是在堆中循环引用的无法回收,如果让let obj1 ={a:obj2};let obj2={a:obj1};这样循环引用的次数不会为零,就不会去回收。 另一个是标记清除策略,从根对象出发标记可达对象,清粗未标记的。此时堆中循环引用的情况就可以进行垃圾回收了。

再一个问题是内存泄漏

内存泄漏指的是程序中不再需要的内存未被释放,导致内存占用持续增长,引发性能下降或崩溃。

常见的内存泄漏有:

1.闭包使用不当,用全局变量声明了一个闭包,如果该页面不关闭的话,闭包里面的变量一直在占用内存。所以说使用完闭包后要及时解除闭包的引用。

2.未清除定时器,比如说设置了一个定时器,没设置清理定时器的代码,当程序执行时定时器就会一直占用内存造成内存泄漏。所以说使用完定时器后要使用clearnInterval进行清除定时器。

3.意外的全局变量,比如说一个变量没有进行声明,在非严格模式下就会自动变成全局变量,这种变量对内存的占用会持续到页面关闭,无法被回收。所以说要开启严格模式 use strict

4.残留的DOM引用,DOM节点被删除后,仍然有js变量去引用,就不会被垃圾回收,内存被占用。所以说严谨一点就需要在删除完获取的dom元素后让dom=null就可以释放内存了。