内存泄漏到底是个什么东西?如何避免内存泄漏

目录

内存泄漏到底是个什么东西?如何避免内存泄漏

一、什么是内存泄漏?

1、内存泄漏

2、GC(垃圾回收)机制是什么?

二、常见内存泄漏场景

1、意外的全局变量

2、被遗忘的定时器/回调

3、闭包未释放大对象

[4、DOM 引用未解绑](#4、DOM 引用未解绑)

[5、被遗忘的监听器、websocket、观察者等(如 IntersectionObserver)](#5、被遗忘的监听器、websocket、观察者等(如 IntersectionObserver))

[6、缓存未清理(Map / WeakMap 使用不当)](#6、缓存未清理(Map / WeakMap 使用不当))

三、结语


作者:watermelo37

CSDN万粉博主、华为云云享专家、阿里云专家博主、腾讯云、支付宝合作作者,全平台博客昵称watermelo37。

一个假装是giser的coder,做不只专注于业务逻辑的前端工程师,Java、Docker、Python、LLM均有涉猎。


温柔地对待温柔的人,包容的三观就是最大的温柔。


内存泄漏到底是个什么东西?如何避免内存泄漏

内存泄漏(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清除一些

javascript 复制代码
function foo() {
  leaked = "I am global!";
}
foo(); // leaked 被隐式挂载在 window 上

2、被遗忘的定时器/回调

闭包中的 userData 永远被回调引用,永远不被释放。

javascript 复制代码
const userData = { name: "Tom" };
setInterval(() => {
  console.log(userData.name); // 闭包引用 userData
}, 1000);

所以应该在组件销毁时清除定时器。

javascript 复制代码
const timer = setInterval(...);
clearInterval(timer);

3、闭包未释放大对象

闭包允许函数访问其创建时的作用域链中的变量,即使这些变量在函数外部已经不可访问。闭包会保留对 createClosure 函数作用域链的引用,这意味着 bigData 也会被保留。

javascript 复制代码
function createClosure() {
  const bigData = new Array(1e6);
  return function () {
    console.log('I am using closure');
  };
}
const closure = createClosure(); // bigData 被保留

解决方案一般有两种思路,避免大对象进入闭包逻辑:

javascript 复制代码
function createClosure() {
  const bigData = new Array(1e6); // 创建一个大数组
  console.log('I am using closure');
}
createClosure();

或者手动释放引用:

javascript 复制代码
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。

javascript 复制代码
const element = document.getElementById('btn');
element.onclick = () => {
  console.log('clicked');
};

应在DOM被销毁时及时解绑事件。

javascript 复制代码
element.removeEventListener('click', handler);

5、被遗忘的监听器、websocket、观察者等(如 IntersectionObserver)

比如元素删除了,observer 还在监听,需要使用 observer.disconnect() 手动解除观察。

javascript 复制代码
const observer = new IntersectionObserver(() => {
  console.log('intersected');
});
observer.observe(document.getElementById('foo'));

6、缓存未清理(Map / WeakMap 使用不当)

Map 会强引用 key 和 value,导致无法释放。致无法释放。

javascript 复制代码
const cache = new Map();
function addToCache(key, value) {
  cache.set(key, value); // 永久引用
}

三、结语

内存泄漏的本质是代码"留下来不该留下来的东西"的体现。通过了解 JavaScript 的垃圾回收机制和闭包原理,我们可以更有意识地优化代码,避免性能隐患。

说实话闭包这个东西,说简单几句话就说完了, 但是要扯到各种作用域,垃圾回收机制,各种作用域创建的函数上下文,可能一万字都讲不清楚,今天就说到这里,有探究想法的读者可以在评论区讨论。

只有锻炼思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~

其他热门文章,请关注:

极致的灵活度满足工程美学:用Vue Flow绘制一个完美流程图

你真的会使用Vue3的onMounted钩子函数吗?Vue3中onMounted的用法详解

DeepSeek:全栈开发者视角下的AI革命者

通过array.filter()实现数组的数据筛选、数据清洗和链式调用

通过Array.sort() 实现多字段排序、排序稳定性、随机排序洗牌算法、优化排序性能

TreeSize:免费的磁盘清理与管理神器,解决C盘爆满的燃眉之急

通过MongoDB Atlas 实现语义搜索与 RAG------迈向AI的搜索机制

深入理解 JavaScript 中的 Array.find() 方法:原理、性能优势与实用案例详解

el-table实现动态数据的实时排序,一篇文章讲清楚elementui的表格排序功能

前端实战:基于Vue3与免费满血版DeepSeek实现无限滚动+懒加载+瀑布流模块及优化策略

MutationObserver详解+案例------深入理解 JavaScript 中的 MutationObserver

JavaScript中通过array.map()实现数据转换、创建派生数组、异步数据流处理、DOM操作等

高效工作流:用Mermaid绘制你的专属流程图;如何在Vue3中导入mermaid绘制流程图

干货含源码!如何用Java后端操作Docker(命令行篇)

在线编程实现!如何在Java后端通过DockerClient操作Docker生成python环境

Dockerfile全面指南:从基础到进阶,掌握容器化构建的核心工具

相关推荐
nbsaas-boot12 分钟前
Java 正则表达式白皮书:语法详解、工程实践与常用表达式库
开发语言·python·mysql
chao_78916 分钟前
二分查找篇——搜索旋转排序数组【LeetCode】两次二分查找
开发语言·数据结构·python·算法·leetcode
一斤代码42 分钟前
vue3 下载图片(标签内容可转图)
前端·javascript·vue
风无雨43 分钟前
GO 启动 简单服务
开发语言·后端·golang
Hellyc44 分钟前
用户查询优惠券之缓存击穿
java·redis·缓存
中微子44 分钟前
React Router 源码深度剖析解决面试中的深层次问题
前端·react.js
光影少年1 小时前
从前端转go开发的学习路线
前端·学习·golang
斯普信专业组1 小时前
Go语言包管理完全指南:从基础到最佳实践
开发语言·后端·golang
中微子1 小时前
React Router 面试指南:从基础到实战
前端·react.js·前端框架
3Katrina1 小时前
深入理解 useLayoutEffect:解决 UI "闪烁"问题的利器
前端·javascript·面试