前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >干货 | 携程桌面应用的前端内存优化与监控

干货 | 携程桌面应用的前端内存优化与监控

作者头像
携程技术
发布于 2020-08-31 14:12:31
发布于 2020-08-31 14:12:31
2K0
举报
文章被收录于专栏:携程技术携程技术

作者简介

吕萌萌,携程资深前端开发工程师,关注前端性能优化与前端框架建设。

一、背景

桌面应用的前端场景不同于传统前端,具有使用者停留时间长,功能复杂且高度聚集在单一页面等特征,因此带来了不同的技术挑战,其中很重要的一点是内存泄漏问题。

1)什么是内存泄漏?

内存泄漏[1](Memory leak)是在计算机科学中,由于疏忽或错误造成程序未能释放已经不再使用的内存。内存泄漏并非指内存在物理上的消失,而是应用程序分配某段内存后,由于设计错误,导致在释放该段内存之前就失去了对该段内存的控制,从而造成了内存的浪费。

2)JavaScript的内存管理

像C语言这样的底层语言一般都有底层的内存管理接口,比如 malloc()和free()。相反,JavaScript是在创建变量(对象,字符串等)时自动进行了分配内存,并且在不使用它们时“自动”释放。释放的过程称为垃圾回收。这个“自动”是混乱的根源,并让JavaScript(和其他高级语言)开发者错误的感觉他们可以不关心内存管理[2]。

3)案例

以携程的IM+项目为例:IM+将多种沟通渠道整合于一体,使客服人员能够全方位地触达用户,提供便捷、全面的服务,进而实现优质的用户体验。所以,在IM+的主页面当中,同时聚集了IM、电话和邮件三大块功能,为了提升坐席的效率和服务质量,还有众多辅助信息模块、回复超时提示模块,也就导致主页面功能非常复杂。

因此,主页面的功能复杂度、代码复杂度都很高,在大量需求的快速迭代期间,一些细节点考虑不够或者某些API使用方式不正确,就会比较容易发生内存泄漏问题。另外,又因为使用者长时间不关闭应用,一旦发生该问题,将会随着时间的推移,泄漏的内存量越积越多,最终影响整个电脑的资源使用情况,造成诸如应用崩溃、电脑卡顿等较为严重的后果。

综上所述,桌面应用的前端开发同学需要额外注意内存的问题,而这个场景在用户停留时间短、功能不重度集中的传统前端页面上基本不存在,所以网络上鲜有这个问题的处理方法。本文提出了一套完整的解决方案,包括:内存占用分析、内存的优化与验证、如何在功能迭代中维持低内存占用,以及线上的内存使用监控。

二、内存占用分析

在此提出两种内存占用分析方法,分别是使用谷歌浏览器的Memory插件分析方法和简单粗暴的单一变量实验分析法。

2.1 使用谷歌浏览器Memory插件分析内存占用

打开谷歌浏览器的调试页面,选择Memory Tab,然后点击Take snapshot获取内存快照,执行一段时间页面操作后,再次Take snapshot,然后对比,可以找到触发内存泄漏的组件(如下图)和独立的dom节点。

使用这个组件的时候,需要注意以下三点:

1)Network的请求、控制台里的日志也会占用Chrome的内存,所以在测试之前,最好把它们清理掉。

2)由于JavaScript的内存管理在语言之内,所以无法确定在获取内存快照之前是否有即将被释放掉的内存,这时可以点击Memory Tab左上角的垃圾回收按钮,手动触发一次垃圾回收,可以确保两次内存快照中都没有即将被清除掉的内存占用。

3)查找detached DOM节点

DOM节点的垃圾回收机制是:当页面的DOM树和JavaScript代码都没有对某个DOM节点的引用时,才可以对其进行垃圾回收。如果一个DOM节点已经被从DOM树中删除,但某些JavaScript变量仍引用该节点,则该节点被称为detached DOM节点,不会被回收。它是内存泄漏的常见原因。

在上图的Memory插件中,可以使用筛选器,输入关键字“Detached”查找分离的DOM树,然后点击DOM可以查看引用它的变量位置。找到之后,可以使用ES6的 WeakSet/WeakMap去解决这个问题。

2.2 二分法查找组件的内存泄漏

上面的方法虽然行之有效,但是对于极其复杂的项目,通过上述方法获取到的内存快照也极其复杂,比较难读,有的时候很难找到各个内存泄漏点,或者即便找到了内存泄漏的组件,也不清楚具体泄漏在了组件的哪一个功能点,哪一行代码上。所以针对这个问题,我们提出了二分法的思路。

首先,针对功能页面,整理总结出高频操作的功能列表,转换成自动化脚本,然后先执行脚本,记录内存占用。之后,在不影响主体功能的情况下,把组件分为两部分,轮流注释掉,分别执行脚本,记录内存占用。最后,对比两批组件的内存占用变化情况,判断内存泄漏主要集中在哪一批组件里。以此类推,可以在确定到组件之后,将二分法降级到功能维度,甚至代码维度,最终找到内存泄漏点。

在实际使用当中,我们综合这两种方法,逐步分块查找,最终解决了内存泄漏的问题。

三、内存优化与验证

3.1 内存的优化

1)可能导致内存泄漏的写法

i. 事件监听未正确移除:采用观察者模式,在组件内部注册监听,或是在一些DOM上注册事件后,需要在组件卸载生命周期中移除监听,否则可能造成内存泄漏。

ii. 组件初始化前/销毁后设置State:组件中存在异步调用,调用完成后触发状态设置,但是在调用完成前组件已销毁,就会产生内存泄漏(控制台会提示:Can’t perform a React state update on an unmounted component. Thisis a no-op, but it indicates a memory lead in your application.)。解决方案:在组件卸载声明周期中将setState置为空函数,或撤销异步调用。

iii. 组件的引用:比如我们的UI确认组件A 在使用完毕后,要释放对来自调用方组件B内部回调函数的引用,因为组件A跟B没有父子关系,所以使用完毕后如果没有释放引用,就会导致组件B不能被销毁,从而导致内存泄漏。

iv. 高频刷新功能集成在大组件中:一些高频刷新的功能,比如说时间显示,最好写在小组件里,不要放出来让它触发大组件的刷新,因为所有的内存泄漏都是积小成多的,如果有内存泄漏,刷新次数越多积攒越多,而大组件因为功能多逻辑复杂,容易内存泄漏,所以高频刷新的功能最好单独写成小组件。

v. 异常处理:未捕获的异常会造成内存泄漏,console.error也会。其实很好理解,异常随便什么时候开调试页面都能看到,就是因为存储在内存里了,所以我们要处理好异常逻辑。

2)React的shouldComponentUpdate生命周期和Immutable、PureRender:存在内存泄漏的时候,减少渲染次数也可以降低内存泄漏的影响。所以针对减少渲染次数的问题,在React框架下,可以采用这样几种方法:

首先,React的shouldComponentUpdate生命周期暴露了钩子,允许用户判断是否需要重新渲染;然后,Immutable可以支持在数据变化的情况下,基于字典序在新地址上复用原有的数据,减少内存占用;最后,PureRender则可以用浅比较自动计算shouldComponentUpdate的结果。

3.2 优化后的验证

1)通过功能埋点分析整理出主要的高频功能。

IM+使用了携程的前端埋点框架,可以分析各个DOM的点击情况,基于点击数据和对业务逻辑的理解,可以获知用户使用的高频功能。

2)基于Selenium实现主流程的自动化测试。

四、在功能迭代中维持低内存占用

1)制定避免内存泄漏的代码规范,在代码审核流程中予以检验。

2)每次发布版本前,长时间循环执行主流程自动化测试,对比测试前后的内存开销。

五、内存使用线上监控

1)调用系统api获取IM+进程的内存开销、总CPU开销、网络延迟等。

2)上报内存、CPU等信息,汇总到ES中。

3)在监控面板中,展示内存、CPU的占用情况。

通过上述优化步骤,IM+桌面应用的内存占用,从之前的随着使用时间快速增长,动辄占用数G,降低到了稳定不变的150M左右。

【引用】

[1]内存泄漏.

https://zh.wikipedia.org/wiki/%E5%86%85%E5%AD%98%E6%B3%84%E6%BC%8F

[2] 内存管理

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Memory_Management

【推荐阅读】

  • 从0到1,搭建一个体系完善的前端React组件库
  • 携程度假无线前端架构演进之路
  • 携程RN渲染性能优化实践
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-08-27,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 携程技术中心 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
页面卡顿?内存泄漏?一文详解如何排查
不知道在座的各位有没有被问到过这样一个问题:如果页面卡顿,你觉得可能是什么原因造成的?有什么办法锁定原因并解决吗?
公众号@魔术师卡颂
2021/05/08
2.9K1
页面卡顿?内存泄漏?一文详解如何排查
「硬核JS」你的程序中可能存在内存泄漏
想来很多同学看到内存泄漏,内心直接会跳出两个字:闭包!!!再让你说点其它的估计就噤声了。如果你对内存泄漏的了解仅限于闭包,那真的是应该仔细看此文了,闭包可能会造成内存泄漏,但是内存泄漏并不是只有闭包,它只是内存泄漏的引子之一罢了。
用户1250838
2021/08/26
1.4K0
小程序的内存管理与垃圾回收机制
在小程序的开发中,内存管理与垃圾回收是保证应用性能和稳定性的关键因素。合理的内存管理不仅能够防止内存泄漏和资源浪费,还能提升应用的响应速度与流畅度。本文将详细分析小程序的内存管理和垃圾回收机制,帮助开发者更好地理解和优化小程序的性能。
LucianaiB
2025/02/21
1270
【JS】324- JS中的内存管理(中高级前端必备)
像C语言这样的底层语言一般都有底层的内存管理接口,比如 malloc()和free()用于分配内存和释放内存。而对于JavaScript来说,会在创建变量(对象,字符串等)时分配内存,并且在不再使用它们时“自动”释放内存,这个自动释放内存的过程称为垃圾回收。因为自动垃圾回收机制的存在,让大多Javascript开发者感觉他们可以不关心内存管理,所以会在一些情况下导致内存泄漏。
pingan8787
2019/08/23
1.5K0
【JS】324- JS中的内存管理(中高级前端必备)
推荐一个检测 JS 内存泄漏的神器
大家好,我是 ConardLi。作为一名 Web 应用程序开发者,排查和修复 JavaScript 代码的内存泄漏一直是最困扰我的问题之一。
ConardLi
2023/01/09
3.9K0
推荐一个检测 JS 内存泄漏的神器
JS内存泄漏排查方法
内存泄漏是一个累积的过程,只有页面生命周期略长的时候才算是个问题(所谓“刷新一下满血复活”)。频繁交互能够加快累积过程,偏展示的页面很难把这样的问题暴露出来。最后,JS逻辑相对复杂才有可能出现内存问题(“bug多是因为代码量大,我自己都hold不住”),如果只是简单的表单验证提交,还没什么机会影响内存
ayqy贾杰
2019/06/12
7.7K0
JS内存泄漏排查方法
Chrome 浏览器垃圾回收机制与内存泄漏分析
JavaScript 引擎会通过向下移动 ESP(记录当前执行状态的指针) 来销毁该函数保存在栈中的执行上下文。
winty
2019/12/20
3.3K0
JavaScript中的垃圾回收和内存泄漏
之前接触的js的内存管理方面的内容一直比较零散,最近在这一块做了一些系统的学习.学习过程中的一些总结在这里分享给大家.欢迎批评指正,共同学习,共同进步.
用户2356368
2019/04/03
1.2K0
JavaScript中的垃圾回收和内存泄漏
Js中常见的内存泄漏场景
内存泄漏Memory Leak是指程序中已动态分配的堆内存由于疏忽或错误等原因程序未释放或无法释放,造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃等严重后果。内存泄漏并非指内存在物理上的消失,而是应用程序分配某段内存后,由于设计错误,导致在释放该段内存之前就失去了对该段内存的控制,从而造成了内存的浪费。对于内存泄露的检测,Chrome提供了性能分析工具Performance,可以比较方便的查看内存的占用情况等。
WindRunnerMax
2020/11/12
2.5K0
浏览器的内存泄漏场景、监控以及分析
虽然前端有垃圾回收机制,但当某块无用的内存,却无法被垃圾回收机制认为是垃圾时,也就发生内存泄漏了
请叫我大苏
2020/02/13
3.6K0
浏览器的内存泄漏场景、监控以及分析
JavaScript 内存泄漏教程
一、什么是内存泄漏? 程序的运行需要内存。只要程序提出要求,操作系统或者运行时(runtime)就必须供给内存。 对于持续运行的服务进程(daemon),必须及时释放不再用到的内存。否则,内存占用越来
ruanyf
2018/04/12
8340
JavaScript 内存泄漏教程
如何排查网页在哪里发生了内存泄漏?
今天我们来学习用 devtool 的 Performance 和 Memory 工具来找出网页哪里发生了内存泄漏。
前端西瓜哥
2022/12/21
5.3K0
如何排查网页在哪里发生了内存泄漏?
实战PerfDog优化小游戏性能
背景: 我们的引擎是Egret,使用的是原生的EUI,转微信小游戏; 工程第一版出来后使用PerfDog测试一波数据。结果发现很多问题,本文主要分两部分
WeTest质量开放平台团队
2020/08/25
1K0
如何解决内存泄漏引发的血案
之前做了一个谷歌浏览器的插件开发,它会打开一个链接,然后收集数据并上传。依次循环,但是跑的时间久了,内存就变得很高,然后浏览器就会变卡,慢慢的影响这个插件的运行,最后浏览器也会崩溃。
零式的天空
2022/03/22
5350
高频面试点:Android性能优化之内存优化(上篇)
链接:https://juejin.im/post/5e72b2d151882549236f9cb8
陈宇明
2020/12/16
1.5K0
高频面试点:Android性能优化之内存优化(上篇)
腾讯QQ桌面版架构升级:内存优化探索与总结
新版桌面 QQ 自内测以来受到许多热心网友和行业人士,以及鹅厂小伙伴的关注,非常感谢大家在内测过程中提的各种有建设性的建议和反馈。其中,也有一小部分有开发背景的用户对我们采用 Electron 框架表达担心:高内存占用、超大安装包、启动缓慢等。究其原因还是担心新版本 QQ 资源占用大、体验变差,针对用户的担心,我们在内存上进行了专项优化,也取得了一些阶段性的进展,在此做一个小结。
腾讯技术工程官方号
2023/08/25
1.1K0
腾讯QQ桌面版架构升级:内存优化探索与总结
有意思的 Node.js 内存泄漏问题
作者:elvinpeng,腾讯 WXG 前端开发工程师 Node.js 使用的是 V8 引擎,会自动进行垃圾回收(Garbage Collection,GC),因而写代码的时候不需要像 C/C++ 一样手动分配、释放内存空间,方便不少,不过仍然需要注意内存的使用,避免造成内存泄漏(Memory Leak)。 内存泄漏往往非常隐蔽,例如下面这段代码你能看出来是哪儿里有问题吗? let theThing = null; let replaceThing = function() {   const new
腾讯技术工程官方号
2020/08/21
6.3K2
深入探索 Android 内存优化(炼狱级别-上)
本篇是 Android 内存优化的进阶篇,难度可以说达到了炼狱级别,建议对内存优化不是非常熟悉的仔细看看前篇文章: Android性能优化之内存优化,其中详细分析了以下几大模块:
做个快乐的码农
2021/11/24
1.5K0
深入探索 Android 内存优化(炼狱级别-上)
一个 Vue 页面的内存泄露分析
什么是内存泄露?内存泄露是指new了一块内存,但无法被释放或者被垃圾回收。new了一个对象之后,它申请占用了一块堆内存,当把这个对象指针置为null时或者离开作用域导致被销毁,那么这块内存没有人引用它了在JS里面就会被自动垃圾回收。但是如果这个对象指针没有被置为null,且代码里面没办法再获取到这个对象指针了,就会导致无法释放掉它指向的内存,也就是说发生了内存泄露。为什么代码里面会拿不到这个对象指针了呢,举一个例子:
用户1272076
2019/03/26
4.1K0
一个 Vue 页面的内存泄露分析
前端常见内存泄漏及解决方案
作者:lzg9527 https://juejin.cn/post/6914092198170460168
ConardLi
2021/02/01
1.1K0
推荐阅读
相关推荐
页面卡顿?内存泄漏?一文详解如何排查
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档