当前位置: 首页 > Web前端 > HTML

内存泄露和eslint内存泄露排查工具开发

时间:2023-03-29 12:24:11 HTML

内存泄露和eslint内存泄露排查工具开发主要内容分为两部分:内存泄露原因和场景案例开发eslint内存泄露排查插件前端内存泄露问题根据案例内存泄漏的原因像js这种自带垃圾回收机制的语言,由于定义了全局对象、事件、定时器,却没有及时清理,导致内存泄漏,导致内存泄漏程序运行时内存使用率更高。全局事件往往是在window和document上添加了事件,没有及时解绑引起的。需要注意的是,绑定事件必须保证addEvenetListener和removeEventListener参数的一致性constf=function(){}//eq1解绑失败,f.bind(this)生成了一个新的函数对象window.addEvenetListener('click',f.bind(this))window.removeEvenetListener('click',f.bind(this))//eq2解绑失败,第三个参数不一致window.addEvenetListener('click',f,false)window.removeEvenetListener('click',f,true)//查看窗口绑定函数getEventListeners(window)全局变量全局变量不一定是指挂在窗口下的对象,在单个js文件中,直接定义全局文件下的文件变量也是全局变量。当一个全局变量在一个代码逻辑生命周期中被修改时,被修改的部分应该在生命周期结束时被移除。从echarts导入'echarts';constobj={};//比较危险的方法,修改全局变量obj,作为导出项,不可控导出constinjectData=(key,val)=>{obj[key]=val;}......//使用第三方库时,要注意文档中关于对象创建的描述//echarts.init生成图表对象时,会将该对象挂载到全局echarts下,并调用dispose方法函数Comp(){useEffect(()=>{constchart=echarts.init(element,undefined,opts);chart.setOption(option);//注释中的代码应该在这里添加//return()=>chart.dispose()},[])}TimerssetInterval、requestAnimationFrame等生成全局定时器的函数需要在退出对应的业务代码生命周期时销毁。正常情况比较好判断,异步递归+setTimeout的场景容易被忽略:timer=setTimeout(()=>{loopFetchData();},3000)}......//clearTimeout(timer)timer=nullwhenexitingthelifecycle如上面的用例所示,似乎timer在退出生命周期的时候被销毁了,但是仔细观察可以发现这个操作忽略了fetchData请求状态。代码状态如下图所示。要正确结束递归调用,需要结束虚线所示的所有异步代码。更合理的做法是定义一个退出循环的判断条件。代码如下lettoStop;asyncfunctionloopFetchData(){constdata=awaitfetchData();如果(停止)返回;timer=setTimeout(()=>{loopFetchData();},3000)}……//进入生命周期时toStop=false;……//退出生命周期时toStop=true循环;so,代码结构如下,整个流程有终端状态eslint内存泄漏排查插件,列出了常见的前端内存泄漏情况。是否可以开发一个eslint插件来协助排查内存泄漏问题?最近开发进度比较紧,没时间把内存泄漏场景的插件全部实现。只开发了递归调用合法性判断插件。eslint插件的开发和原理网上比较多。参考文章递归调用合法性判断插件。定时器不回收的问题,我们可以发现无论是同步还是异步的递归调用,最好包含return,避免不必要的问题,所以开发了相关的插件。地址如下:代码地址对应规则地址。测试方法:全局安装pnpm,进入代码仓库。pnpminstall后执行以下操作cd./test-pkgspnpmlink../eslint-plugin-memory-leaknpxeslint./可以在控制台查看test-pkgs/index.js文件的报错信息递归判断函数之间的关系。假设函数的引用关系如下。如何快速判断任意两个节点之间的关系(包括,a在b之前等)?我们可以在dfs的同时,给每个节点加上进入和退出的计数。以节点a为例,进入时,将起始值标记为count,然后进入子节点d,将d标记为起始值count+1,退出d,将结束值标记为count+2,然后退出a,a标示结束值count+3...可见,子节点的起始值和结束值必须是父节点的起始值和结束值的子集,a节点必须在b节点之前,所以必须满足a.end>b.start。在大多数库生成的ast树中,节点包含start和end参数,可以用来判断代码块之间的关系;标记起始值和结束值后,只需要O(1)的时间就可以得到节点之间的关系。函数引用的有向图是否成环假设函数是一个节点,方向是指函数的用法,例如:functiona(){b()c()},函数的方向有向图表示为a->[b,c]可以发现函数的使用逻辑整体是一个有向图,我们需要做的是判断有向图中是否存在环路.值得注意的是,这里判断有向图是否包含环与常见的同类算法题不同的是,不需要在生成整个有向图之后进行判断,而是在生成有向图的过程中进行判断。每增加一个结点,都要确定无向图中找环的UnionFind方法:N(0)->1,2,3N(1)->0,2N(2)->0,1N(3)->0,4N(4)->3由于N(2)∈(N(0)∪N(1)),可以确定节点0、1、2构成一个环。将此逻辑应用到有向图上函数之间的关系如下,可以使用dfs+banList获取每个函数的使用项集合,判断函数子集是否包含自身:N(0)->2,3,N(2),N(3)N(1)->0,N(0)N(2)->1,N(1)N(3)->4,N(4)注:想想这里的md语法好像有问题,弄了半天也没有改正。.###总结由于开发时间不够,水平有限,目前只完成了eslint内存泄漏检测插件的一小部分,相关处理还有很多需要完善的地方,会随着相关内容的学习和开发。