发布网友 发布时间:2024-09-27 21:55
共1个回答
热心网友 时间:2024-10-17 19:43
引言什么是内存泄露?
系统进程不再用到的内存,没有及时释放,就叫做内存泄漏(memoryleak).当内存占用越来越高,轻则影响系统性能,重则导致进程崩溃.站在前端的视角.写的不好的JavaScript可能出现难以察觉且有害的内存泄露问题:
白屏/黑屏
卡顿/页面加载迟缓
闪退/发热
?
哪些操作会造成内存泄露一、意外声明全局变量
意外声明全局变量是最常见但也最容易解决的内存泄露问题.在window上创建的属性,只要window本身不被清除就不会消失.无法做到有效地释放清除
代码编写
functiont(){name='renlingxin'}//name变量会挂在到window上而window上的变量在窗口关闭或重新刷新页面之前并不会销毁//用letconstvar代替其他挂载
插件/业务场景中有时会通过挂载到window的属性实现运行环境下不同工程之间地相互通信例如
WebView/JavaScript
Cocos/JavaScript
SDK-A/SDK-B
...
我们需要合理正确的挂载.避免挂载大体积的数据.
二、闭包
functiond(){letname='renlingxin'returnfunctionr(){console.log(name)}}//这会导致分配给name的内存被泄露.如果name很大.就会是一个大问题.三、定时器(做到及时的clear)
保持良好的代码习惯.
<template><div>{{timeDom}}</div></template><script>mounted(){this._time=setInterval(()=>{this.timeDom=newDate()},1000);},//beforeDestroy销毁之前的定时器beforeDestroy(){clearInterval(this._time)}</script>四、事件*(例如vue的eventBus)
importeventBusfrom'./eventBus'exportdefault{mounted(){//添加eventBus*eventBus.$on('page-change',()=>{console.log('page-changetriggered!')})},beforeDestroy(){//移除eventBus*eventBus.$off('page-change')}}五、变量和变量/变量和Dom相互引用
我们来看一个例子:
在控制台执行node--expose-gc
>process.memoryUsage(){rss:143835136,heapTotal:5054464,heapUsed:3446128,external:1668772,arrayBuffers:9917}>lety=newArray(5*1024*1024)//1.创建一个5*1024*1024大小的数组undefined>letg={d:y,g:'renlingxin'}//2.设置g中的key-d值为y建立强引用关系undefined>process.memoryUsage()//3.查看当前内存为43.48M左右{rss:325320704,heapTotal:47263744,heapUsed:45596216,external:1668828,arrayBuffers:9933}>y=g.d=null//4.将y和g.d设置null解除引用关系null>global.gc()//5.手动触发一次gc回收undefined>process.memoryUsage()//6.内存缩小至3.02M左右{rss:140541952,heapTotal:5316608,heapUsed:3171248,external:1668812,arrayBuffers:9917}以上我们可以得出结论变量之间的相互引用以及主动释放引用关系从而触发GC回收的重要性
可以使用weakMap/weakSet建立弱引用关系
>process.memoryUsage()//1.操作之前查看初始内存大小约为3.28M{rss:144654336,heapTotal:5054464,heapUsed:3447472,external:1668772,arrayBuffers:9917}>lett=newWeakMap()//2.新建WeakMapundefined>lety=newArray(5*1024*1024)//3.新建5*1024*1024的数组undefined>t.set(y,'333')//4.设置y至WeakMap//WeakMap{<itemsunknown>}>process.memoryUsage()//5.当前内存大小约为43.22M{rss:329957376,heapTotal:47001600,heapUsed:45320184,external:1668824,arrayBuffers:9929}>global.gc()//6.主动触发gc回收undefined>process.memoryUsage()//7.内存无变化{rss:332529664,heapTotal:47263744,heapUsed:45110040,external:1668812,arrayBuffers:9917}>y=null//8.将y原始值清空为null.但是这里我们并没有去清空t中的ynull>global.gc()//9.释放内存undefined>process.memoryUsage()//10.查看内存大小约为3.07M{rss:161005568,heapTotal:5316608,heapUsed:3227656,external:1668815,arrayBuffers:9920}六、console(浏览器会记忆输出的变量。避免在线上环境打印)
uglifyjs-webpack-plugin/transform-remove-console...等方法都可以实现移除log
调试工具一、Performance
全面的性能监控工具
名词解释
Summary:各指标时间占用统计报表
Bottom-Up:事件时长排序列表
Calltree:事件调用顺序列表
EventLog:事件发生的顺序列表
Ps:当你的JsHeap有了这样的走势.那么就会有内存泄露的风险
二、Memory
快照功能
开始拍照
全部删除
功能:
Summary(概要)-通过构造函数名分类显示对象;
Comparison(对照)-显示两个快照间对象的差异;
Containment(控制)-可用来探测堆内容;
Constructor(构造函数)表示所有通过该构造函数生成的对象
Distance到GCroots(GC根对象)的距离。GC根对象在浏览器中一般是window对象,在Node.js中是global对象
Shallowsize列显示了由对应构造函数生成的对象的shallowsizes(直接占用内存)总数---对象自身的大小,不包括它引用的对象单位是字节
Retainedsize列展示了对应对象所占用的最大内存----对象自身的大小和它引用的对象的大小,即该对象被GC之后所能回收的内存大小。单位是字节
内存类型
Compiledcode编译代码
closure闭包
HTMLDivElement、HTMLAnchorElement、DocumentFragment等等这些其实就是你的代码中对元素的引用或者指定的DOM对象引用...
三、Performancemonitor
实时监控工具.参考指标有JavaScript内存/CPU占用率/DOM节点/事件监听...
结论内存泄露这个话题在前端提及的频率不是很多,大多数的业务场景是简单的页面h5的开发,但对于逻辑复杂/沉淀多/动画丰富等场景内存管理便尤其重要,并且造成内存泄露的原因很多是积少成多的问题叠加,造成积重难返的境遇,因此需要我们从日常写码中重视内存管理。