• 转---单页面应用下的JS内存管理


    正文从这开始~

    内存问题对于后端童鞋而言可能是家常便饭,特别是C++童鞋。我在实习时做过半年的c++游戏客户端开发(也是前端开发哦),也见识了各式各样的内存问题,就说说我的第一个坑,当时做个需求,就是在玩家名字上加些酷炫的封号(就是下图中红框中的图片),人小不懂事,频繁去new一个图像出来,却不去释放它,导致客户端跑个几个小时就会崩溃,虽然很快就被揪出来修复掉,却也留下了心理阴影(5年了,依然历历在目啊)。

    话题回到前端开发,很幸运,js还是拥有比较完善的gc机制,使得我们不需要时时刻刻像C++那样去关心内存问题。甚至说在传统网页开发中,极少需要去关注内存泄漏问题,因为即使有些许内存泄漏,页面刷新又将内存拉回起点。但随着SPA的流行,特别是移动app的火爆,js的内存问题也许会成为体验的瓶颈。

    什么是内存泄漏

    来自wikipedia的解释:“由于疏忽或错误造成程序未能释放已经不再使用的内存”。其实在有gc的高级语言中,我们可以定义得更仔细一点:“当我们不再使用某块内存的时候,却不让gc去回收它“。

    js的内存管理机制以及一些常见的js内存泄漏点,本文就不一一介绍了,网上上已经有不少好的文章将这些点讲的很清楚。有兴趣的可以自行搜索。

    什么样的内存泄漏是致命的

    我写了一个内存泄漏的公式:

    (n: 总共n个内存泄漏的地方;m:单个内存泄漏点所占用的内存;f:单个内存泄漏点的频次)

    web前端的内存泄漏不像server端,server端哪怕一个极小的内存泄漏,都会因为海量的请求被放大(f极大),内存泄漏对于server端来说是致命的。但对于web前端来说,大部分的操作都很低频,也就是说很多时候,即使有内存泄漏,但是在整个页面生命周期里面,内存泄漏只是也只是增长了很少一部分,对用户体验很难造成影响。那么什么样的情况会导致致命issue?

    我们来分析下上面公式中中的三个因子(很明显三个因子都对结果都是正相关关系):

    1. m(单次内存泄漏的内存量)

    无论是前端还是后端,想要单次泄漏较大量的内存还是不容易的,即使有这样的情况发生,想要找出根源也相对比较容易的,这种情况请开发不要作死。

    2. f (单个内存泄漏频次)

    我在km上搜到的前端js(不包含node)内存实战记录,zone miniportal 内存泄漏分析和经过和MiniPortal JS内存泄漏定位二(内部文章),虽然是09年的事情了,但我认为还是非常有代表性的。论大小pv上报返回的image对象,ajax获取的xml文件这能有多大?2k?但是在长时间打开的qzone页面上被一次又一次的执行,终成影响体验的大问题。

    我也粗略总结下前端的一些高频操作(欢迎补充):

    • 强交互应用中的ajax或者socket请求处理

    • setInterval, requestAnimationFrame等循环处理

    • click等高频处理函数

    我处理过的内存泄漏问题中,bc的情况其实是很少的,主要致命的内存泄漏出现在a情况,因为大部分请求都可能伴随着页面的局部刷新,特别是引入第三方框架时(比如chart相关,highchart、d3等),仅仅remove相应dom一般不会释放第三方对象,这时候就需要手动进行destroy。

    例:用vue包装的highchart组件需要在组件生命周期末端进行销毁

    反例:某知名报表系统, 页面如下:

    点击刷新后监控内存变化,下图中四组内存快照分别是初始,请求20次,请求40次,请求60次时的内存情况(有兴趣的可以用timeline进行实验)。因为每次刷新的数据是一致的,将snap4雨snap3进行比较,按照大小进行分类会看到有不少每20个一组的内存块,再查看它的gc路径,泄漏点就显而易见了,真的是chart先动手的。

    想象一下你的移动webapp在刷页面是刷占了300M的内存。。。。。。。。

    3. n(总共n个内存泄漏的地方)

    在传统网页中,单个页面中的内存泄漏点还是屈指可数的,但是在SPA中,页面的跳转并没有刷新,上个页面的内存泄漏点很有可能也不会被释放,这就会导致内存会一直递增,在某个阶段爆发,这正是SPA所面临的内存挑战。

    举个非常普遍的例子Popup,因为点击其他区域时,需要隐藏弹出框,我们会在document上绑定click事件来处理隐藏的逻辑,如果在组件被销毁时,没有去解绑该事件的话,就导致了该处理函数中的引用无法被释放,在这个例子中这个popup对象都不会被释放。现在最流行的vue组件库element ui就存在这样的问题,谁有空可以去提个issue。

    正面例子:在生命周期末期记得unbind

    最后,今天的文章长度会适中吗?可能你还需要了解看看以下为你推荐的文章:

    【第591期】4类 JavaScript 内存泄露及如何避免

    【第528期】了解 JavaScript 应用程序中的内存泄漏

  • 相关阅读:
    PHP通用函数
    Discuz 取各排行榜数据
    htaccess 增加静态文件缓存和压缩
    一个域名解析不同访问方法
    TP5:隐藏inde.php文件
    vscode:解决操作git总让输入用户名及密码问题
    vscode:配置git
    cmd:相关命令和笔记
    PHP:通过MVC,实现第三方登录(百度)
    Linux:301重定向 —— 将不带www的重定向到带www的
  • 原文地址:https://www.cnblogs.com/AlexBlogs/p/6501743.html
Copyright © 2020-2023  润新知