+18888889999
诚信为本:市场永远在变,诚信永远不变。

前端内存优化知多少?内存泄露只是冰山一角

你的位置: 首页 > 百威资讯

前端内存优化知多少?内存泄露只是冰山一角

2024-06-18 21:13:17

前端开发者往往不太关注页面所占用的内存,因为前端内存的分配与回收基本是依靠系统自动完成的,这个过程对于开发者是无感的

但内存优化的作用也同样重要,一个好的网站,内存的优化也是极致的,比如淘宝的首页,只有 大小

特别是随着前端项目的逐渐复杂,内存的占用也逐渐攀升,曾经遇到过一个项目:页面的内存超 ,同时打开几个窗口,就导致了页面崩溃,从此开始重视内存优化

淘宝.png

之前有这种误解:认为只有内存泄露的时候,才需要去优化内存。然而,经过项目实践,发现很多场景下都有内存优化的必要,而内存泄露只是比较极端的场景之一

比如,页面中总有一些大数据渲染的场景,除了会造成页面渲染变慢外,所占用的内存也是巨大的

以下页面,左侧是一个 Tree 树形控件,该控件一次性加载了三千条数据。难以置信,该页面的内存竟然到了,而改为懒加载子节点后,该页面的内存直接降到了,内存的前后差异是惊人的

treeList.png

使用 Memory 工具,通过的方式,分析当前页面的内存使用情况(熟悉这块的同学可以直接跳到下面的优化建议)

1)打开chrome浏览器控制台,选择 Memory 工具

2)点击左侧 start按钮,刷新页面,开始录制 ,会生成页面加载过程内存变化的柱状统计图(蓝色表示未回收,灰色表示已回收)

memory.jpg

Constructor:对象的类名;
Distance:对象到根的引用层级;
Objects Count:对象的数量;
Shallow Size: 对象本身占用的内存,不包括引用的对象所占内存;
Retained Size: 对象所占总内存,包含引用的其他对象所占内存;
Retainers:对象的引用层级关系

通过以下测试代码来了解 Memory 各关键项的关系


Tom.png

shallow size 和 retained size 的区别,以用红框里的 和 更直观的展示

Tom 的 shallow 占了16M,retained 占用了 28M,这是因为 retained 包括了引用的指针对应的内存大小,即 所占用的内存

所以 Tom 的 retained - shallow=12M,正好跟 Jane 占用的 12M 相同

retained size 可以理解为当回收掉该对象时可以释放的内存大小,在内存调优中具有重要参考意义

通过JS堆动态分配时间线,找到内存最高的节点,分析这些时刻执行了哪些代码,发生了什么操作,尽可能去优化它们

1)从柱状图中找到最高的点,并选中它们

2)按照 Retainers size(总内存大小)排序,点击展开内存最高的哪一项,点击展开构造函数,可以看到所有构造函数相关的对象实例

3)选中构造函数,底部会显示对应源码文件,点击源码文件,可以跳转到具体的代码,这样我们就知道是哪里的代码造成内存过大

4)结合具体的代码逻辑,来判断这块内存变大的原因,重点是如何去优化它们,降低内存的使用大小

retainedSize.jpg

点击可以跳转到具体的源码

localkey.png

大数据渲染始终是前端的一大难题,DOM 渲染会占用很大的内存,非常吃性能

根据笔者的以往案例,这往往是导致页面崩溃的首要原因,特别是对于电脑或手机配置低的用户

breakdown.png

可以通过:数据懒加载、组件懒加载、虚拟滚动、数据分页等方式,来减少组件的 DOM 渲染

在 window 上添加的监听事件,在页面卸载时要主动移除,并注意移除的正确性


在页面销毁的时候,主动解绑,释放内存


对于函数节流与防抖的场景,要特别注意:确保移除的是同一个事件,如果姿势不对,可能依旧会造成内存泄漏


这段代码的写法是错误的,因为每次调用时, 其实都会返回一个新的函数,导致 addEventListener 和?removeEventListener 方法传入的回调函数已经不是同一个函数,监听器没有被正确移除

正确的写法:



我们总会在调试代码时,加上很多 console 打印。以下代码,因为 list 数组被 console 所引用,导致 list 内存不能被释放


console.png

经过验证,只有 devtools 打开时,console 打印才会引起内存泄漏的,如果不打开控制台,console 是不会引起内存变化的。稳妥起见,需要在生产环境时使用插件过滤掉 console 打印



上一篇文章 闭包用多了会造成内存泄露?90%的人都理解错了 解释了项目中大量使用闭包,并不会造成内存泄漏,除非是错误的写法

错误的写法:闭包所引用的变量在函数外部。因为开发者需要非常小心,否则稍有不慎就会造成内存泄露,我们总是希望可以通过 gc 自动回收,避免人为干涉

正确的写法:闭包引用的变量定义在函数中。这样随着外部引用的销毁,该闭包就会被 gc 自动回收 (推荐),无需人工干涉



举个例子


在页面卸载的时候可以考虑解除引用



有些情况,需要手动清除引用。但有时候一疏忽就忘了,所以才有那么多内存泄漏

ES6 推出了两种新弱引用的数据结构: 和 。它们对值的引用都是不计入垃圾回收机制的,如果其他对象都不再引用该对象,那么gc 会自动回收该对象所占用的内存


注册监听事件的 listener 对象很适合用 WeakMap 来实现。


代码 2 比起代码 1 的好处是:由于监听函数是放在 WeakMap 里面,一旦 element 对象的其他引用消失,与它绑定的监听函数 handler 所占的内存也会被自动释放

内存优化虽然是前端性能优化中比较冷门的方向,但通过对页面的内存分析,并尝试优化它,可以帮助我们更好的了解自己的项目

当面试官问起:“你的页面内存有多少,有哪些方面去优化它?”时,可以结合自己的实践,深入的讲解我们关于内存优化的理解

参考文章:
万恶的前端内存泄漏及万善的解决方案
JavaScript 内存机制(前端同学进阶必备)
V8 内存浅析

文章系列地址:github.com/xy-sea/blog

文中如有错误或不严谨的地方,请给予指正,十分感谢。如果喜欢或有所启发,欢迎 star

地址:海南省海口市玉沙路58号  电话:0898-66889888  手机:18888889999
Copyright © 2012-2018 首页-百威娱乐-官方注册站 ICP备案编:琼ICP备88889999号 

平台注册入口