svga/SVGAPlayer-Web-Lite

内存泄漏问题

Opened this issue · 15 comments

在h5页面如果频繁调用动画,或者动画一直loop播放的话,会造成内存泄漏,页面直接卡死或者自动刷新页面。这个问题有什么解决办法吗?

最好提供一个最小复现,方便问题定位

这个问题我也遇到了 导致页面卡死

最好提供一个最小的恢复,方方便问题定位

`
import React, { memo, useRef, useEffect } from 'react';
import { Parser, Player } from 'svga';

const SvgaComponent = (props) => {
const { svgaUrl, loop = 1, onEnd, fillElement } = props;
const svgaRef = useRef(null);
const parserRef = useRef(null);
const playerRef = useRef(null);
useEffect(() => {
  playAnimation();
  return () => {
    parserRef?.current?.destroy();
    playerRef?.current?.destroy();
  };
}, []);

const playAnimation = async () => {
  try {
    parserRef.current = new Parser();
    const svga = await parserRef.current?.load(svgaUrl);
    playerRef.current = new Player({
      container: svgaRef.current,
      loop: loop
    });

  // 需要填充的动态元素
  for (const key in fillElement) {
    if (Object.hasOwnProperty.call(fillElement, key)) {
      const canvasEl = fillElement[key];
      canvasEl && (svga.dynamicElements[key] = canvasEl);
    }
  }

    await playerRef.current?.mount(svga);
  } catch (error) {
    console.error(error);
  }

  playerRef.current.onEnd = () => {
    console.log('end');
    onEnd?.();
  };
  // 开始播放动画
  playerRef.current?.start();
};

return (
  <canvas
    style={{
      width: '100%',
      height: 'auto',
      ...props?.style
    }}
    ref={svgaRef}
  ></canvas>
);
};
export default memo(SvgaComponent);

`

这里是我封装的svga组件,我就是在外部进行调用此组件,动画可能是loop循环的,也可能是点击频繁播放的就会导致页面的卡顿白屏或者刷新

@codingJia 我对 react 不太熟悉,destroy 的部分会及时执行吗?

@codingJia 我对 react 不太熟悉,destroy 的部分会及时执行吗?

会的,每次都会销毁,不过如果是loop的播放就不会销毁了,因为他会一直执行动画。

在h5页面如果频繁调用动画,或者动画一直loop播放的话,会造成内存泄漏,页面直接卡死或者自动刷新页面。这个问题有什么解决办法吗?

这个确实是个bug,我试过很小的svga文件webview中循环播放,必现重刷,有时会webview崩掉。测试机型 iphone8p,系统16,我的解决方法是回退到1.3.1版本。。。。

测试用例最好避免使用上其他的框架,我提供了一个最小的 测试用例。大家可以看看有没有其他使用上的疑问。

通过这个测试用例,在浏览器端或者内存检查工具 memlab,暂无发现存在内存泄漏的情况。

image

image

通过 fork 这个最小的测试用例进行修改测试,如有发现其他出现内存泄漏的情况,麻烦带上重现用例 repo。

测试用例最好避免使用上其他的框架,我提供了一个最小的 测试用例。大家可以看看有没有其他使用上的疑问。

通过这个测试用例,在浏览器端或者内存检查工具 memlab,暂无发现存在内存泄漏的情况。

image

image 通过 fork 这个最小的测试用例进行修改测试,如有发现其他出现内存泄漏的情况,麻烦带上重现用例 repo。

我在使用你提供的测试例子使用了区间在400到700kb上下的svga文件进行循环播放,在ios16.4版本中会出现播放卡死或者页面刷新现象。
shakeBoxSvga.svga.zip
这是我使用的svga文件

ME1686651546251.mp4

这是使用上边svga文件出现的页面刷新视频

这个问题在ios16.4.1上百分百复现(13pm,14pm均复现)开启isCacheFrames可以避免这个问题,个人认为是OffscreenCanvas导致的崩溃具体错误我还没抓到,如果要不开启缓存可尝试改写 player/index.ts drawFrame
不缓存没必要使用OffscreenCanvas进行离屏计算
` private drawFrame (frame: number): void {
if (this.videoEntity === undefined) throw new Error('Player VideoEntity undefined')
if (this.config.isUseIntersectionObserver && !this.isBeIntersection) return

this.clearContainer()

const context = this.config.container.getContext('2d')
if (context === null) throw new Error('Canvas Context cannot be null')

if (this.config.isCacheFrames && this.cacheFrames[frame] !== undefined) {
  const ofsFrame = this.cacheFrames[frame]
  // ImageData
  // context.putImageData(ofsFrame, 0, 0)
  context.drawImage(ofsFrame, 0, 0, ofsFrame.width, ofsFrame.height, 0, 0, ofsFrame.width, ofsFrame.height)
  return
}

if(this.config.isCacheFrames) {
  let ofsCanvas = this.ofsCanvas

  // OffscreenCanvas 在 Firefox 浏览器无法被清理历史内容
  if (window.OffscreenCanvas !== undefined && window.navigator.userAgent.includes('Firefox')) {
    ofsCanvas = new window.OffscreenCanvas(this.config.container.width, this.config.container.height)
  }

  ofsCanvas.width = this.config.container.width
  ofsCanvas.height = this.config.container.height

  render(
    ofsCanvas,
    this.bitmapsCache,
    this.videoEntity.dynamicElements,
    this.videoEntity.replaceElements,
    this.videoEntity,
    this.currentFrame
  )

  context.drawImage(
    ofsCanvas,
    0, 0, ofsCanvas.width, ofsCanvas.height,
    0, 0, ofsCanvas.width, ofsCanvas.height
  )
    // 把帧缓存起来
  if ('toDataURL' in ofsCanvas) {
    const ofsImageBase64 = ofsCanvas.toDataURL()
    const ofsImage = new Image()
    ofsImage.src = ofsImageBase64
    this.cacheFrames[frame] = ofsImage
  } else {
    this.cacheFrames[frame] = ofsCanvas.transferToImageBitmap()
  }
} else {
  render(
    this.config.container,
    this.bitmapsCache,
    this.videoEntity.dynamicElements,
    this.videoEntity.replaceElements,
    this.videoEntity,
    this.currentFrame
  )
}

}`

目前尝试使用 iPhone 12 mini / iOS 16.3.1 Safari 浏览器打开 testcase 没发现崩溃情况,同时使用 mac safari 进行 debug 也没发现内存泄漏的问题,后续尝试升级 iOS 16.5 再试试。

default.mp4

@wentoos 可否提个 PR,通过设置参数关闭使用 OffscreenCanvas?

@wentoos可以否提个PR,通过设置参数关闭使用OffscreenCanvas?

目前没抓到准确为OffscreenCanvas导致崩溃现场,debug时看不到堆栈,因为直接刷新了(我也很无奈,为了解决这个问题,我也是逐一排查的.....),mac 控制台工具也抓不到safari错误信息,可以知道的是 16.4.1必现,稍后我可以提交一个pr上去,最快速就是魔法一下 window.OffscreenCanvas = undefined 帮助大家先解决问题

如果是频繁去点击某个按钮来控制动画的显示隐藏(这里的显示隐藏指的是canvas元素在页面中被移除,svga也执行了销毁。)也会出现页面卡死的状态

svga的格式转换之后,用pag格式播放,解决大动画crash的问题,而且内存释放也没问题

的格式转换之后,用pag格式播放,解决大动画crash的问题,而且内存释放也
怎么转换呢,请问