Cesium编程中内存占用的性能分析

903 阅读4分钟

cesium介绍

最近在用Cesium做GIS(Geographic Information System)编程。Cesium是一个基于WebGL,用来在浏览器中创建一个3D的地球或2D地图的开源javascript库。同时,还可以在地球上绘制一些图形,拿多边形来举例。

cesium渲染图形.jpg

上图为Cesium渲染多边形的流程,通过顶点坐标生成了多边形结构实例,再传入多边形结构的构造函数中,就可以生成多边形结构的实例了。而且第二行右侧新建颜色也是一个需要创建一个颜色实例,再传入创建多边形实例的方法中的,就可以生成一个具体的多边形实例。也就是说,只要换一个颜色参数,同样的多边形结构实例可以生成不同的过变形实例。把需要同时显示的多个多边形实例放入到一个primitive中,来控制显隐后。再把primitive加入到cesium维护的primitives中。此时,在cesium中,会维护这个primitive的引用。如果想要把这个primitive对象回收,需要通过viewer.scene.primitives.remove()来移除。

以上介绍完了Cesium和通过Cesium来渲染多边形的一个例子。下面简单介绍一下业务,就是每个不同的时间节点都要渲染一批多边形。每批多边形的位置和形状是不随着时间而改变的,但是颜色会随着时间而改变,而且每个时刻多边形的数量可能会变。

问题一:图形实例化的分散

在页面初始化时,就开始加载所有时刻的primitive。但是加载到中间的时候浏览器会崩溃,显示如下页面:

截屏2022-08-22 上午9.48.46.png

通过浏览器的性能tab来分析:

截屏2022-08-17 下午2.45.08.png

可以看到JS的堆内存确实是增长很快。而且可以看到内存增长趋势最大的时间段,正好是执行createWaterMeshPrimitives函数的时候。如果可以初步判断出createWaterMeshPrimitives中新增了哪些实例产生了内存消耗,可以先对这些新增实例的地方加以分析。因为此时浏览器的内存消耗已经很大,内存快照很可能还没生成,浏览器就崩了。而且用构建简单mock数据模拟确实需要一些时间。所以可以先通过堆内存增长所对应的函数来分析。

所以我们可以把问题定位在createWaterMeshPrimitives函数。此函数中消耗内存最多的就是创建多边形实例了,虽然对于多边形几何体实例和颜色实例程序中都有缓存,但是函数中一起实例化了所有时间的多边形,如果时间点过多,还是会导致内存崩溃。

对于这种问题可以对程序进行优化,一开始只加载前几个时间点的多边形,随着时间的后移,再去预加载后面时间点的多边形。随着时间点的前移,逐渐把经过时间点的多边形实例进行销毁,来释放内存。

问题二:垃圾回收不完整

改完了渲染策略以后,发现一开始加载的时候浏览器不会崩溃了。但是随着时间点的前移,到中后段的时候,浏览器又会崩溃。打开性能分析面板,继续观察:

截屏2022-08-22 下午4.44.59.png

发现随着时间前移,堆内存确实还是会逐步增加,直到最后阶段达到最大值,浏览器崩溃。我们放大来看,内存明显增长的阶段浏览器经历了什么:

截屏2022-08-22 下午5.08.17.png

可以看到两段明显的内存增长阶段,都是在执行addWaterMeshPrimitive函数。而在主要垃圾回收阶段之后,内存占用还是没有回到最初的水平。可以初步判断,我们在addWaterMeshPrimitive函数中实例化的多边形实例在销毁之后,并没有真正被回收。可以想到我们在销毁多边形实例的时候,除了给对象赋为null,还应该调用viewer.scene.primitives.remove()来移除primitive,这样内存才能被回收。我们来看一下修改之后的内存情况:

截屏2022-08-22 下午5.03.01.png 最后可以看到内存消耗正常了。