前端内存泄漏的分析案例
在最近给客户交付的一个项目中,客户反映系统跑一段时间就会奔溃掉。我的第一直觉就是可能发生了内存泄漏。
复现bug
为了能够让bug复现,我让开发的小伙,先把模拟系统在本地跑起来。要知道,bug复现是很重要的。bug复现,能够让开发人员直观的感受到bug发生的过程。另外如果bug不能复现,程序员的反应可能是这样的:
我的这边是好的呀,没有什么问题。
哎呀,是不是你的使用方法不对啊。
看到了吗,如果bug不能复现,会直接极大的减少程序员想要修复bug的意愿的。
我后来问他,测试的怎么样,他说没有问题呀。我问题跑了多久,他说有一会儿吧,看没问题就关闭了。
一般来说,内存泄漏最终导致奔溃的需要挺长一段时间的,所以我告诉他一直跑,跑到奔溃为止。
后来第二天,他说果然奔溃了。
查找原因
因为更新了的版本才出现内存泄漏的问题,所以有理由怀疑,就是新增加的功能导致的内存泄漏。 新加的功能是,在一个TWaver的表格中,部分单元格中使用echart 图表来绘制。而由于代码中每次刷新的都会调用echart.init方法重新创建。导致原来创建的echart实例中创建的一些变量不能释放,所以这里怀疑,echart 如果反复删除并创建会导致内存泄漏。所以让开发人员改成了缓存的方案。
当然由于TWaver表格的内部机制,会在重新绘制的时候,移除上一次单元格的内容,并移除内容所有父子关系。这个问题导致了,缓存方案失效。 最终的解决方案是重写了这个移除方法,不解除移除内容的父子关系。
到此为止,应该问题是解决了。让小伙伴改进后,继续把模拟系统一直跑起来。 很不幸的是,第二天仍然奔溃。由于此时并不能直观的看出问题所在,所以需要使用一些技巧了。
简化程序
可以确定的是,还是表格的问题导致的内存泄漏,为了避免干扰,让小伙伴把表格的核心内容拉出来写两个一个demo。 这样就可以专注于表格相关问题的定位了。
在解决一些大项目的问题的时候,如果发现一些难定位的问题,可以考虑把程序简化,抽出有问题的代码部分写小的例子。方便问题的定位,排除不必要的干扰。
使用chrome的工具分析
首先把小伙伴写的小例子跑起来,然后打开chrome的控制台。点击memory标签:
![memory标签](https://upload-images.jianshu.io/upload_images/6271001-fa1489291ab7caa3.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/512)
选择Allocation instrumentation on timeline,点击start,开始录制内存的记录情况,会发现有一些蓝色的条,永远不会变暗,表示这部分内存始终未被回收:
![内存记录情况](https://upload-images.jianshu.io/upload_images/6271001-a4747af3879a9506.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/512)
点击其中的一个蓝条,可以查看局部的内存情况,如下图所示:
![内存记录情况2](https://upload-images.jianshu.io/upload_images/6271001-bf8076d0a5e82158.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/512)
从中可以看到有array,system和Detached HTMLSpanElement。 我们知道Detached HTMLSpanElement表示已经脱离文档树的dom元素,它也是导致内存泄漏的一个经常的诱因,点击Detached HTMLSpanElement,查看详情:
![Detached HTML Element](https://upload-images.jianshu.io/upload_images/6271001-312172f3dc54236c.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/512)
可以看到"_stringPool"这样的一个数组对象,里面放的就是Span元素,引用他的就是table,而且数量有372个之多,由于小例子使用的表格只有一行一列,这么大的数量,肯定是内存泄漏导致,直接在浏览器打印这个pool,可以看出这个数量确实在一直增加(看index的情况):
![内存泄漏](https://upload-images.jianshu.io/upload_images/6271001-c982353001923c48.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/512)
至此,查到了是那个地方发生了内存泄漏。 根据这个线索,比较容易定位到程序的逻辑问题所在,并很快就解决了问题。
ps:同时还发现了table.__divPool也有内存泄漏的情况,通过同样的方式定位到了这个问题并最终解决。
问题出在小伙伴对于renderCell方法的重载和release方法的重载,这个涉及到TWaver的内部逻辑,此处就不详细说明了。
欢迎关注公众号:ITman彪叔
相关文章
- 如何让Redis兼顾效率、成本和运维?看vivo优化出妙招
- 微服务-技术专区-链路追踪(pinpoint)-部署使用
- 梳理总结-备份整理-知识点问题梳理
- MySQL-技术专区-mysql数据库权限管理
- MySQL-技术专区-数据库权限管理
- 开源软件的盈利模式
- 分布式-技术专区-Redis分布式锁实现-第二步
- Groovy系列(5)- Groovy IO操作
- 分布式-技术专区-Redis分布式锁实现-第一步
- JPA接口整理归纳方法规则
- 备份整理-实用归纳
- Groovy系列(4)- Groovy集合操作
- Groovy系列(3)- Groovy基础语法
- Zookeeper-技术专区-运作流程分析介绍
- 技术哲学-技术定位和思想
- Groovy系列(1)- Groovy简述
- Jmeter系列(30)- 性能指标(3) | 性能指标峰值
- Jmeter系列(29)- 性能指标(2) | 并发数
- 分布式-技术专区-Redis并发竞争key的解决方案详解
- Jmeter系列(28)- 性能指标(1) | 常见性能指标