Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

内存泄漏问题 #84

Open
codingJia opened this issue May 26, 2023 · 14 comments
Open

内存泄漏问题 #84

codingJia opened this issue May 26, 2023 · 14 comments

Comments

@codingJia
Copy link

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

@Naeemo
Copy link
Contributor

Naeemo commented May 26, 2023

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

@simuusang
Copy link

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

@codingJia
Copy link
Author

codingJia commented May 30, 2023

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

`
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循环的,也可能是点击频繁播放的就会导致页面的卡顿白屏或者刷新

@Naeemo
Copy link
Contributor

Naeemo commented May 30, 2023

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

@codingJia
Copy link
Author

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

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

@guozongwei
Copy link

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

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

@lijialiang
Copy link
Contributor

lijialiang commented Jun 2, 2023

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

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

image

image

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

@codingJia
Copy link
Author

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

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

image

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

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

ME1686651546251.mp4

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

@wentoos
Copy link

wentoos commented Jun 20, 2023

这个问题在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
  )
}

}`

@lijialiang
Copy link
Contributor

lijialiang commented Jun 21, 2023

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

default.mp4

@lijialiang
Copy link
Contributor

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

@wentoos
Copy link

wentoos commented Jun 25, 2023

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

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

@codingJia
Copy link
Author

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

@mambofish123
Copy link

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants