浅谈垃圾回收、内存泄漏与闭包

什么是垃圾?

在js中,垃圾通常指的是不再被程序使用的内存或对象。也就是说,垃圾是指程序中分配的内存空间或对象,但不再被程序使用或无法被访问到的内容

function createIncrease() {
   
  const doms = new Array(100000).fill(0).map((item, i) => {
   
    const dom = document.createElement("div");
    dom.innerHTML = i;
    return dom;
  });
  function _increase() {
   
    doms.forEach((dom) => {
   
      dom.innerHTML = Number(dom.innerHTML + 1);
    });
  }
  return _increase;
}
const increase = createIncrease();
const btn = document.querySelector("#btn")
btn.addEventListener("click",increase)

上面的代码当执行createIncrease方法时,会创建一个increase方法,同时也会创建一个拥有100000个元素的doms数组,当btn点击时执行increase方法。此时的doms肯定不能称之为垃圾,因为当btn点击时需要使用doms,这种判断相对来说比较简单

const nums = [1, 2, 3, 4, 5];
const sum = nums.reduce((total, num) => total + num, 0);

上面的代码sum是由nums的每一项相加得出的结果,至于nums还会不会再次被使用,根本没法判断
因此,从上面对垃圾的阐述以及例子可以看出,是否为垃圾是由开发者自己决定的。通俗点说,当一段代码执行完成后不会再被使用,那么可以称之为垃圾

垃圾回收

垃圾回收是JavaScript中一种自动内存管理机制,用于检测和释放不再使用的对象,以便回收内存。垃圾回收器会定期扫描内存中的对象,并标记那些仍然被引用的对象,然后清除未被引用的对象垃圾回收器。换句话说,垃圾回收只回收那些开发者自己都访问不了无法触达的内存空间

垃圾回收算法:JavaScript 垃圾回收器使用不同的算法来确定内存中的对象是否可以被回收。常见的算法包括标记-清除引用计数增量标记等。标记-清除是最常用的算法,它通过标记不再需要的对象,然后清除它们来回收内存

function createIncrease() {
   
  const doms = new Array(100000).fill(0).map((item, i) => {
   
    const dom = document.createElement("div");
    dom.innerHTML = i;
    return dom;
  });
  function _increase() {
   
    doms.forEach((dom) => {
   
      dom.innerHTML = Number(dom.innerHTML + 1);
    });
  }
  return _increase;
}
const increase = createIncrease();
const btn = document.querySelector("#btn");
const flag = true;
function handleClick() {
   
  flag = false;
  if (flag) {
   
    increase();
    btn.removeEventListener("click", handleClick);
    increase = null;
  }
}
btn.addEventListener("click", handleClick);
const nums = [1, 2, 3, 4, 5];
const sum = nums.reduce((total, num) => total + num, 0);
nums = null

比如上面的代码:

  • btn被点击时执行increase方法,当点击完成后,移除btn点击事件以及将increase设置为null,此时就会被垃圾回收器回收
  • nums重新赋值为null时,那么之前声明的nums的内容我们就无法再访问,因此就会被回收

内存泄漏

内存泄漏是指当程序中的对象不再被使用,但由于某些原因仍然被保留在内存中,导致内存占用不断增加,最终耗尽可用内存的现象。

const timer = setTimeout(() => {
   
  // todo
}, 100);
const nums = [1, 2, 3, 4, 5];
const sum = nums.reduce((total, num) => total + num, 0);

在这里插入图片描述
上面的代码我们声明了一个timer的定时器和一个nums的数组,timer没有清理,nums我们明确以后不会再使用,但是timernums会一直存在内存中,这样就容易引起内存泄漏

常见的内存泄漏情况包括:

  • 未及时清除的计时器如果你创建了一个计时器,但忘记在不再需要时清除它,它将一直存在并持有对相关对象的引用,导致内存泄漏
  • 未解绑的事件监听器如果你绑定了一个事件监听器,但在元素被销毁之前忘记解绑它,这个监听器将一直存在,并可能持有对相关对象的引用,导致内存泄漏
  • 循环引用如果对象之间存在循环引用(例如,对象 A 持有对象 B 的引用,同时对象 B 也持有对象 A 的引用),即使它们不再被使用,垃圾回收器也无法释放它们的内存,导致内存泄漏

当内存泄漏过大时会严重影响我们的代码,因为在开发时为避免内存泄漏,可以采取以下措施:

  • 尽量避免创建不必要的闭包,当不再需要时,显式地解除对闭包的引用
  • 将不再使用的变量设置为 null,以协助垃圾回收
  • 尽量避免循环引用,特别是在闭包中
  • 仔细管理计时器、事件监听器和其他可能持有对对象的引用的机制
  • 使用优化的数据结构和算法,避免不必要的内存占用

闭包

闭包是指一个函数能够访问和操作在其词法作用域以外定义的变量。闭包在JavaScript 中被广泛应用,它可以用于创建私有变量、实现模块化等。由于闭包会使函数引用外部变量,当闭包存在时,垃圾回收器可能无法释放被闭包引用的对象,导致内存泄漏

function createIncrease() {
   
   const doms = new Array(100000).fill(0).map((item, i) => {
   
     const dom = document.createElement("div");
     dom.innerHTML = i;
     return dom;
   });
   function _temp() {
   
     return doms;
   }
   function _increase() {
   }
   return _increase;
 }
 let increase;
 btn.addEventListener("click", () => {
   
   increase = createIncrease();
 });

上面的代码当点击事件被执行时对increase重新赋值,而createIncrease方法返回_increase方法,因此,此时doms是一块无法触达的内存空间,那么此时的这块无法触达的内存空间会被回收吗?
未点击之前
在这里插入图片描述
点击后
在这里插入图片描述
从上面的图片可以看出,doms并不会被回收,这是因为_temp_increase方法的闭包环境是一样的,它们共用一个词法环境,因此doms不会被回收


从上面的例子可以看出,闭包导致内存泄漏的原因有:

  • 持有了不再需要的函数引用,会导致函数关联的词法环境无法销毁,导致内存泄漏
  • 当多个函数共享词法环境时,会导致词法膨胀,从而使一些无法触达的内存空间无法回收,导致内存泄漏

相关推荐

  1. 前端理论总结(js)——内存泄漏

    2024-02-08 06:06:01       20 阅读
  2. 记一个导致的内存泄漏问题

    2024-02-08 06:06:01       11 阅读
  3. js进阶-es6-作用域-垃圾回收机制--变量提升

    2024-02-08 06:06:01       18 阅读
  4. Python的内存管理垃圾回收机制

    2024-02-08 06:06:01       39 阅读
  5. 内存泄漏内存溢出

    2024-02-08 06:06:01       27 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-02-08 06:06:01       18 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-02-08 06:06:01       19 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-02-08 06:06:01       18 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-02-08 06:06:01       20 阅读

热门阅读

  1. 为自己的项目媒体资源添加固定高度

    2024-02-08 06:06:01       32 阅读
  2. linux系统非关系型数据库redis的配置文件

    2024-02-08 06:06:01       29 阅读
  3. MySQL的MVCC机制

    2024-02-08 06:06:01       36 阅读
  4. 前端开发 :(二)HTML基础

    2024-02-08 06:06:01       26 阅读
  5. MySQL常用命令

    2024-02-08 06:06:01       26 阅读
  6. Mongodb聚合:$planCacheStats

    2024-02-08 06:06:01       26 阅读
  7. 人工智能之估计量评估标准及区间估计

    2024-02-08 06:06:01       31 阅读