1.背景
桌面程序对内存消耗要求很高,基本的要求是整个程序控制在500M以内。
这就要求每个功能点的内存消耗尽可能地少,大于50M的内存消耗就要想办法优化。
2.现状
gif的显示会导致程序的内存激增。以数字大脑用的雷达图动画为例进行说明,下面这个图片的尺寸为4.45M。
2.1. 使用Image组件显示
在javafx中,使用如下代码进行显示:
未显示gif的内存如下:
显示gif时的内存如下:
小结:未显示gif前的内存为15M,显示了gif之后的内存为125M,大约占用内存110M。
2.2. 对比Chrome
而这张图片放到chrome中显示的话,仅占用24M。
3.目标
以chrome的内存占用作为标杆。目标是将内存占用控制在25M以内。
4.分析
先看一下是否是javafx的bug。通过google,发现 javafx 2 在显示gif的时候存在内存泄露的bug,但 javafx 8 中这个bug已经被修复了,当前用的是 javafx 8,故而没有内存泄露。
通过阅读源码发现,gif图片在加载的时候,会将所有的帧都加载到内存中,如下图所示:
示例的gif图片总共有185帧,尺寸为370370,每个像素点占用空间为4B(RGBA),占用内存:185 370 370 4 ≈ 97M。
原因小结:Image组件会将gif的所有的帧都加载至内存中,这直接导致了内存占用过高。
5.对策
5.1. Gif图像有损处理
- 方案一,减少gif图片的尺寸和帧数。比如:将长和宽都减少为原来的1/2,则占用的内存将只有原来的1/4。比如:减少帧的数量为原来的1/2,则内存占用将只有原来的1/2。
5.2. Image组件改造
针对image组件进行改造。
- 方案二,Image组件或者其依赖的类中,有无参数可以配置为不加载所有帧至内存中。
- 方案三,github等平台上有无其它Image组件,不加载所有帧至内存中的。
5.3. 其它加载动画的方式
我们的目的是显示动画,动画显示方式有多种,可以是gif,也可以视频、javafx动画、svg等。
- 方案四,将gif转换为mp4,然后调用MediaPlayer来播放mp4。
- 方案五,使用javafx 的动画api来编写动画。
- 方案六,将gif转换为svg,然后播放。
6.计划
从方案一至方案六依次尝试。
7.跟进
方案一,缺点:减少尺寸,可能图片会变模糊,尤其在高分屏下面。减少帧数量,可能会使动画变得不流畅。如果其它方案都不行了,再用这种方案。
方案二,找不到相关的配置参数。
方案三,找不到相关的组件。
方案四,将gif转成mp4后再播放,效果非常好,占用内存约为2M。下面详细介绍这种方法。
7.1. gif转换成mp4
使用在线转换器来将gif转换为mp4,链接如下:
https://convertio.co/zh/gif-mp4/
7.2. 代码
7.3. 效果
未播放mp4之前的内存如下:
播放mp4时的内存如下:
通过对比,可以发现内存占用大约为2M。
7.4. 小结
使用mp4来播放动画内存占用较少,效果很好,超出了之前设定的目标。
剩下的2个方案,方案四和方案五,以后有时间的话,再去尝试