内存泄漏-如何避免内存泄漏

内存泄漏——海量数据背后隐藏的项目生产崩溃风险!如何避免内存泄漏
        内存泄漏(Memory Leak)是 Web 开发中常见但容易忽视的问题。随着项目体量增长或长时间运行的单页应用增多,内存泄漏所带来的性能下降、页面卡顿甚至崩溃问题变得尤为突出。

        本文将带你深入理解内存泄漏的成因,常见场景,以及应对方案,助你写出更加稳健高效的前端应用。

一、什么是内存泄漏?
1、内存泄漏
        内存泄漏指的是:程序中某些对象已经不再被需要,但由于某种原因仍然被引用,导致垃圾回收器无法释放这些内存。

2、GC(垃圾回收)机制是什么?
        现代 JavaScript 引擎如 V8 采用 “可达性算法(Reachability)”,当某个对象从根对象(如 window、全局变量、闭包引用等)开始无法被访问到,它就会被回收。

        有些情况下会绕过垃圾回收机制,那些已经不需要但是不会被回收的变量等就会一直保存在内存中,会持续的浪费内存,直到进程被杀死才会彻底清除。这就是内存泄漏产生的原因。

二、常见内存泄漏场景
1、意外的全局变量
        函数内如果不使用 let / var / const 声明变量,就会直接挂载到 window 对象上。所以需要尽量避免直接写变量,或者开始‘ use strict ’严格模式,避免隐式全局变量。

        尽量不要使用 var 定义变量,在全局作用域里面(即函数外)通过 var 定义的变量均会挂载在 window 对象上,不会被销毁,会持续造成内存泄漏。

        尽量使用 const  / let 来定义变量,哪怕是在全局作用域里面通过 const  / let 定义的变量,也只会挂载在 Script 作用域中。即全局词法环境(Lexical Environment) 中,这是一种抽象的作用域管理方式,浏览器自己维护的,你访问不到它,但 JavaScript 引擎知道该怎么找。相比于直接通过 var 挂载到window对象上,相对更好被GC清除一些

function foo() {
  leaked = "I am global!";
}
foo(); // leaked 被隐式挂载在 window 上
2、被遗忘的定时器/回调
        闭包中的 userData 永远被回调引用,永远不被释放。

const userData = { name: "Tom" };
setInterval(() => {
  console.log(userData.name); // 闭包引用 userData
}, 1000);
        所以应该在组件销毁时清除定时器。

const timer = setInterval(...);
clearInterval(timer);
3、闭包未释放大对象
        闭包允许函数访问其创建时的作用域链中的变量,即使这些变量在函数外部已经不可访问。闭包会保留对 createClosure 函数作用域链的引用,这意味着 bigData 也会被保留。

function createClosure() {
  const bigData = new Array(1e6);
  return function () {
    console.log('I am using closure');
  };
}
const closure = createClosure(); // bigData 被保留
        解决方案一般有两种思路,避免大对象进入闭包逻辑:

function createClosure() {
  const bigData = new Array(1e6); // 创建一个大数组
  console.log('I am using closure');
}
createClosure();
        或者手动释放引用:

function createClosure() {
  const bigData = new Array(1e6); // 创建一个大数组
  return function () {
    console.log('I am using closure');
    bigData = null; // 手动释放引用
  };
}
const closure = createClosure();
closure(); // 调用闭包,释放 bigData
4、DOM 引用未解绑
        DOM 被移除时,如果事件监听没有解绑,闭包可能继续引用 DOM。

const element = document.getElementById('btn');
element.onclick = () => {
  console.log('clicked');
};
        应在DOM被销毁时及时解绑事件。

element.removeEventListener('click', handler);
 5、被遗忘的监听器、websocket、观察者等(如 IntersectionObserver)
        比如元素删除了,observer 还在监听,需要使用 observer.disconnect() 手动解除观察。

const observer = new IntersectionObserver(() => {
  console.log('intersected');
});
observer.observe(document.getElementById('foo'));
6、缓存未清理(Map / WeakMap 使用不当)
        Map 会强引用 key 和 value,导致无法释放。致无法释放。

const cache = new Map();
function addToCache(key, value) {
  cache.set(key, value); // 永久引用

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

流氓也是种气质 _Cookie

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值