当前位置: 首页 > news >正文

【每日一面】如何解决内存泄漏

基础问答

问:有没有遇到过内存泄漏?怎么排查处理的

答:前端页面上出现内存泄露,使用 Chrome devtools -> memory 工具排查,选择时间轴分配(Allocations on timeline)功能后开始录制操作,在页面上进行相关组件的操作,停止录制后,查看内存曲线,重点关注内存曲线上升的和下降的位置,如出现只升不降,没有明显回落的区域,再重点操作,重新录制对应位置的操作,逐步缩小定位。对于这种重点关注的区域,可以同时使用堆快照追踪持续增长的对象。对排查出来的点位进行验证的时候,可以通过内存面板的垃圾回收按钮,如下图,回收后如果内存大小还是很高,可以确认是存在无法回收的内存,有泄露的情况。
image-20251116025955-kdes7f7-tmiqyaxt.png

扩展延伸

内存泄漏是 JavaScript 开发中隐蔽性强且影响严重的问题,尤其在长生命周期应用,如 SPA、后台管理系统中,可能导致页面卡顿、崩溃甚至浏览器无响应的问题。

内存泄露的本质是:本来应该被回收的对象因为意外的引用而保留了下来,导致垃圾回收器无法释放这个对象所占用的内存,使得内存占用持续增长。

垃圾回收机制

JavaScript 采用自动垃圾回收机制,不需要手动释放内存,通过引用计数标记-清除算法回收不再使用的内存:

  • 引用计数:跟踪每个对象被引用的次数,次数为 0 时回收,但是出现循环引用的时候,这个就无法解决了。
  • 标记 - 清除:从根对象(如 window )出发,标记所有可达对象,未被标记的对象将被回收,这是目前浏览器主流的算法。

OOM

和内存泄露相关联的还有一个概念,即OOM,内存溢出,指的是在程序申请内存时,发现没有可用内存分配,直接抛出了 OOM 异常。

一般来说,内存泄露是内存溢出的一个原因,但不是唯一的原因,而内存泄露会持续消耗内存资源,最终导致没有可以分配的内存给程序,出现 OOM。

内存泄露的场景

  1. 意外的全局变量

    一般是在非严格模式下出现,使用的变量没有声明,会隐式的绑定到 window 对象上,变成持久性的引用,如:

    function fn() {data = {};
    }
    

    解决方案:对于这种情况,第一优先的是启动严格模式(现在的框架或项目都是默认为严格模式,通常不需要关注),其次,在现在使用的 es6 规范下,优先使用 let/const​ 关键字声明,最后如果真的是全局变量,我们应该在确定不再使用后,赋值为 null ,从而切断对象的引用,让 GC 自动回收。

  2. 闭包导致内存泄露

    对于前端,闭包是一个非常好用的特性,但同时也需要在使用的时候注意,如果创建的闭包被长期使用,则闭包持有的变量就无法释放,一个经典案例就是计时器:

    function handleOnClickFac() {let timer = null;return function () {timer = setInterval(() => {console.log('hello');}, 3000);}
    }window.clickBtn = handleOnClickFac();btn.addEventListener('click', window.clickBtn);
    

    在这里,每次点击按钮都会触发定时器的创建,但是我们没有清除回收,所以导致这个定时器一直存在,每次点击的时候都会创建一个新的定时器。

    这个例子中,包含两个场景,一是闭包,二是定时器。

    解决方案:限制闭包生命周期,比如这里在 btn 组件卸载时,销毁闭包,从而实现“不可达”的情况,让 GC 回收,其次需要在使用完成后,清除闭包内的引用,在这个例子中,我们不仅要清楚引用,同时还应该清除定时器,否则依旧存在问题。

  3. DOM 元素引用未释放

    分两种情况:1. DOM 树中已经没有 DOM 元素了,但是 JavaScript 中还有这个 DOM 元素的链接(变量),2. 事件监听器没有移除,存在 DOM 和监听回调存在互相引用的情况。

    // 场景1:DOM已删除但 JS 仍引用
    const list = document.getElementById('list');
    const data = { element: list }; // 引用DOM元素
    document.body.removeChild(list); 
    // list已从DOM树移除,但data.element仍引用它,无法回收// 场景2:事件监听器未移除
    const button = document.getElementById('btn');
    button.addEventListener('click', () => {console.log('点击事件');
    });
    // 按钮被删除后,监听器未移除,导致按钮和回调函数都无法回收
    

    解决方案:解决这类场景的核心依旧是在不需要的时候释放引用,不过对于 DOM,还有一种方式就是使用事件委托,从而在子元素删除的时候不受影响。

  4. 第三方库资源未清理

    类似于 Echarts 、地图等库,会要求我们在不使用的时候,调用对应的销毁的 API,如果我们没有调用,这些库创建的临时资源就会持续占用内存,导致内存泄露。

这些场景下的解决方案都是需要我们手动在需要的地方去清除引用,从而使 GC 能够识别并回收内存,通过这些例子也不难发现,虽然在 JavaScript 中不需要我们做类似于 C++ 的手动内存回收,但是依旧需要我们去帮助 GC 更好的判断资源是否需要回收。

检测和分析

内存泄露的检测和分析主要是通过浏览器的内存工具,这里以 Chrome 为例,我们在检测和分析时使用的是 Chrome Devtool Memory 面板:
image-20251116153104-s5tb0qs-scjvtike.png

  1. 观察时间线上的分配(Allocation Timeline)

    1. 开启记录后,按照推测的问题,操作页面内容,完成后停止记录,开始自动分析
    2. 观察只升不降的区域,重复录制该区域对应的操作,查看内存是否确实存在只分配不回收的情况,记录该操作
  2. 记录堆快照(Heap Snapshot)

    1. 操作开始前,记录一次初始的堆快照

    2. 重复第一步记录的操作,拍摄第二次快照,并开启比较(Comparison)模式,重点关注 Delta 和 Retainers 指标(这里对应的面板的中文名是 #增量​ 和 固定装置 ,翻译不是很准确,这里提供英文界面的图作为参考

      image-20251116153926-8vvr07i-edfpjyti.png
      Delta 关注持续增长的对象,Retainer 追踪引用该对象的变量

  3. 点击垃圾桶(代表 GC)触发一次 GC,如果 GC 后内存依旧很高,就可以确认是存在内存泄露。

面试追问

  1. 内存泄露和内存溢出有什么关系?

    内存泄露会导致内存溢出,但是内存溢出不一定是内存泄露导致的。

  2. 常见的内存泄露场景,举个例子?

    参考本文【内存泄露的场景】一节

  3. Node.js 服务中,长生命周期对象持有短生命周期对象是一个典型的泄露场景,举例并给出排查思路

    // 用全局对象做缓存,无淘汰策略
    const cache = {}; // 接口每次请求都往缓存加数据
    app.get('/api/data', (req, res) => {const key = `data_${req.query.id}`;const largeData = fetchLargeData(req.query.id); // 10MB 数据cache[key] = largeData; // 只加不删,缓存持续膨胀res.send(largeData);
    });
    

    由于 cache 没有设置缓存的过期时间、淘汰的方式,导致 largeData 一直被持有,使得内存不断增长。

    排查思路:1. Node.js 应用启动时添加 --inspect 标志,2. 在 Chrome 浏览器中,访问 chrome://inspect 链接对应的 Node 进程,开始监测,3. 记录初始时的堆快照和多次触发后的堆快照,方式参考【检测和分析】一节,4. 查看 cache 的引用路径以及清理逻辑。5. 设置缓存时间或LRU淘汰策略解决这个问题

  4. 线上环境 Nodejs OOM 触发报警了,你应该怎么做?

    首先,应急止损,滚动重启服务,避免损失扩大,同时增加内存延缓 OOM 时间。

    其次,分析问题出现的时间,判断是否可以回滚服务解决。

    最后,分析定位根源,按照服务日志和本地排查手段进行。

    如果使用的是 k8s 等虚化手段,可以配置服务重启规则,避免人工低效的操作方式。

http://www.jsqmd.com/news/44332/

相关文章:

  • 2025年热门的刀刮布篷布设备最新TOP品牌厂家排行
  • 2025年11月geo优化服务商推荐榜:十大服务商综合实力与行业口碑全解析
  • 2025年11月豆包搜索排名优化推荐:十大服务商综合评测与技术实力全解析
  • 2025年新疆残膜回收机公司权威推荐榜单:棉花残膜回收机/北疆残膜回收机/粉杆收膜打包一体机源头厂家精选
  • 2025 最新火花机厂家推荐榜:新型 / 镜面 / 数控 / 五轴联动等全品类优选,权威测评助力精密加工选型
  • 力扣算法 2154题 将找到的值乘以2
  • 2025年11月geo优化公司排行榜:头部服务商技术迭代与长期发展指南
  • 78.15kW光伏系统采用扰动观察法实现最大功率点跟踪的解决方案
  • 2025年知名的150吨地磅厂家推荐及采购参考
  • 2025年松木猫砂生产厂家权威推荐榜单:无尘矿砂/豆腐猫砂/水晶猫砂源头厂家精选
  • 2025年11月geo优化公司排行榜:十大优质服务商综合能力与发展前景指南
  • 2025年新疆大马力拖拉机公司权威推荐榜单:凯尔3004拉犁/新疆拖拉机/凯尔拖拉机源头厂家精选
  • Vue学习251119
  • 2025年11月geo优化公司排行榜:基于多维度评估的十大优质服务商选择指南
  • 2025年评价高的GY1B25ADM比例阀厂家最新推荐排行榜
  • Ubuntu18.04安装Grafana12
  • 2025年11月豆包关键词排名优化热度榜:行业头部企业服务能力全景评测
  • 2025年11月geo公司排行榜:基于行业标准的十大服务商综合排名与选择策略
  • 11.19工作总结
  • 2025年11月豆包关键词排名优化对比分析:基于权威数据的十大企业综合评估
  • 2025年11月geo公司排行榜:十大领先企业服务能力与长期发展指南
  • 2025年11月geo服务商排行榜:十大优质企业技术实力与服务指南
  • 2025年质量好的酒炮PC管行业内口碑厂家排行榜
  • 2025年热门的全景激光切割机厂家推荐及选购参考榜
  • 2025年北京无极太极机构权威推荐榜单:万元礼物/无极太极站桩/太极站桩源头机构精选
  • 2025年11月AI搜索优化排行榜:十大领先企业综合实力与行业应用指南
  • 2025年11月北京geo优化公司排行榜:十大优质服务商综合能力与长期发展指南
  • 2025年靠谱的手摇丝杆升降机厂家推荐及选购指南
  • 2025 云南靠谱旅行社推荐榜单:避开消费陷阱,选对服务商就看这篇
  • 2025年11月北京geo优化公司排行榜:十大领先企业长期发展能力与服务体系指南