分析样本
- 开发环境
- 项目:百川 2
- 仪表盘 _ 图表数:小林疋仪表盘测试 _ 3、第一个仪表盘-001 * 5
- 动作:反复切换 10 次;
分析方式:
仪表盘之间反复切换,使用Performance
记录性能火焰图;分析出具体原因之后使用Memory
找出具体泄露的代码;
具体步骤
根据分析方式的交互,随着反复切换仪表盘,可以看到JS heap
和Listeners
直线飙升,可以分析出出现卡顿的原因确实是由于内存泄露导致的,没有在正确的时机销毁\释放掉内存和事件监听;
同样,通过Memory
分析发现,在时间轴中能体现出内存占用出现高峰后并没有被释放:
挑时间轴中的一个高峰发现能看懂的就这些:
就先从这些入手,能看到对象的引用大都在dataSource
、chartRefs
、dashboardDataRefs
上:
代码修改
主要逻辑是在组件销毁时将引用置为 null,例如:
// BIChartDesigner/CanvasRender.js
useEffect(() => {
// 这里是点击下钻和查询按钮的收口
chartDataRef.current.refreshChartData = async ({ drillField }) => {
resetPagination();
await requestData({
isEdit,
chartDataRef,
activeComponentId,
filters,
panel_id,
drillField,
fromScreenShot,
setLiteLoading,
global_param_list,
queryParamsRef,
});
};
return () => {
chartDataRef.current = null;
};
}, [chartDataRef, activeComponentId, filters, panel_id, global_param_list]);
同理,在dashboardDataRef.current
赋值的位置将其释放;
改完之后看效果:
使用相同的分析方式,JS heap
从原来的87.5~309
减到85.1~237
,但是内存还是在增长的趋势,还要继续分析;
在Memory
可以看到图表相关的数据:
盲猜图表可能也发生了内存泄露,并且事件监听最有可能的就是绑定在图表上,我们使用的图表是@visactor/vtable@1.3.1
,首先看下 Google 上有没有相关问题搜索,果然,在 github 上就有人提出过 vtable 是有内存释放的问题,并且在issue中也有解答,不过官方在1.3.0这个版本就已经修复了:
修复方式:
我们的版本使用的是 1.3.1,按道理应该是更新到了,为什么我们的就没办法释放呢,看源码更改的地方是packages/vtable/src/core/BaseTable.ts
是不是由于我们使用的是 react 版本没有更新到这个更改呢?于是想到可以在提出这个 bug 的大佬的 codepen 基础上再复现一下,看是不是由于版本的原因:
当我看到tableInstance.release()
的时候我就感觉离解决不远了,原来图表实例上是有释放的方法的,果然,在我们的代码中,实例化图表之后再销毁时,并没有调用 release 方法,于是在引用了@visactor/vtable
组件的地方的useEffect
中调用:
useEffect(() => {
...
tempDataRef.current.ins = new ListTable(containerRef.current, options);
...
return () => {
tempDataRef.current.ins.release();
}
}, [...])
在重新跑分析方法可以看到,JS heap
降到了81.4~162
,并且从图中可以分析出内存事件监听和内存都得到了释放:
再看看Memory
分析:
可以看到每次内存高峰上面都有被释放的标记(最后一次是因为还停留在那个页面),通过时间轴对比,可以看出从最初的 110s 完成 10 次切换,速度提高到 55s 完成 10 次切换,速度提高了一倍;
至此,内存泄露的问题得到初步解决,还有对象释放,以及其他图表类型的优化上还有空间;