系统相关
首页 > 系统相关> > javascript – 总画布内存使用超出最大限制(Safari 12)

javascript – 总画布内存使用超出最大限制(Safari 12)

作者:互联网

我们正在开发一个使用d3-force在画布上绘制网络的visualization web application.
由于每个节点都包含大量信息,并且由于CPU占用大量资源来重绘每个帧的所有内容,因此我们实现了一种缓存,其中每个节点都在其画布上绘制(未链接到DOM),每个缩放级别一次.然后在节点的位置在主画布(链接到DOM)上绘制那些画布.

我们对速度增益感到满意,即使结果现在是内存密集型(特别是在hidpi显示器(视网膜)上,像素密度可以是2或3).

但现在我们在iOS上遇到了浏览器的问题,在与界面进行少量交互后,进程崩溃了.
对于我的回忆,这对于旧版本(iOS12之前)来说不是问题,但是我没有任何未更新的设备来确认这一点.

我认为这段代码总结了这个问题:

const { range } = require('d3-array')

// create a 1MB image
const createImage = () => {
    const size = 512

    const canvas = document.createElement('canvas')
    canvas.height = size
    canvas.width = size

    const ctx = canvas.getContext('2d')
    ctx.strokeRect(0, 0, size, size)
    return canvas
}

const createImages = i => {
    // create i * 1MB images
    let ctxs = range(i).map(() => {
        return createImage()
    })
    console.log(`done for ${ctxs.length} MB`)
    ctxs = null
}

window.cis = createImages

然后在iPad和检查员中:

> cis(256)
[Log] done for 256 MB (main-a9168dc888c2e24bbaf3.bundle.js, line 11317)
< undefined
> cis(1)
[Warning] Total canvas memory use exceeds the maximum limit (256 MB). (main-a9168dc888c2e24bbaf3.bundle.js, line 11307)
< TypeError: null is not an object (evaluating 'ctx.strokeRect')

存在,我创建256 x 1MB画布,一切顺利,但我再创建一个,canvas.getContext返回一个空指针.
然后无法创建另一个画布.

该限制似乎与设备相关,因为在iPad上它是256MB而在iPhone X上它是288MB.

> cis(288)
[Log] done for 288 MB (main-a9168dc888c2e24bbaf3.bundle.js, line 11317)
< undefined
> cis(1)
[Warning] Total canvas memory use exceeds the maximum limit (288 MB). (main-a9168dc888c2e24bbaf3.bundle.js, line 11307)
< TypeError: null is not an object (evaluating 'ctx.strokeRect')

因为它是一个缓存我应该能够删除一些元素,但我不是(因为将ctxs或ctx设置为null应该触发GC,但它不能解决问题).

我在这个问题上找到的唯一相关页面是webkit源代码页:HTMLCanvasElement.cpp.

我怀疑问题可能来自webkit本身,但我想在发布到webkit问题跟踪器之前确定.

有没有其他方法来破坏画布上下文?

提前感谢任何想法,指针,……

EDITS:

为了添加一些信息,我尝试了其他浏览器. Safari 12在macOS上也存在同样的问题,即使限制较高(计算机内存的1/4,如webkit源中所述).我也试过最新的webkit build(236590)而没有更多的运气.
但该代码适用于Firefox 62和Chrome 69.

我改进了测试代码,因此可以直接从调试器控制台执行.如果有人可以在较旧的Safari(如11)上测试代码,那将非常有用.

let counter = 0

// create a 1MB image
const createImage = () => {
    const size = 512

    const canvas = document.createElement('canvas')
    canvas.height = size
    canvas.width = size

    const ctx = canvas.getContext('2d')
    ctx.strokeRect(0, 0, size, size)
    return canvas
}

const createImages = n => {
    // create n * 1MB images
    const ctxs = []

    for( let i=0 ; i<n ; i++ ){
        ctxs.push(createImage())
    }

    console.log(`done for ${ctxs.length} MB`)
}

const process = (frequency,size) => {
    setInterval(()=>{
        createImages(size)
        counter+=size
        console.log(`total ${counter}`)
    },frequency)
}


process(2000,1000)

解决方法:

我花了一个周末制作一个简单的网页,可以快速显示问题.我已经向Google和Apple提交了错误报告.该页面会显示一张地图.您可以平移和缩放所有想要的内容,Safari检查器(在iPad上运行Web,使用MacBook Pro查看画布)看不到画布.

然后,您可以点击一个按钮并绘制一条折线.当你这样做时,你会看到41幅画布.平移或缩放,你会看到更多.每个画布都是1MB,因此在您拥有256个孤立画布之后,当iPad上的画布内存已满时,错误就会开始.

重新加载页面,点击按钮放置一个多边形,同样的事情发生.

同样有趣的是我添加了按钮来为白天和黑夜的地图添加样式.当它只是一张地图时(或者只有标记的地图,有一个按钮可以在地图上显示一些标记),你可以来回走动.没有孤儿画布.但画一条线,然后当你改变造型时,你会看到更多孤立的画布.

在Active Monitor中查看MacBook上的Safari,在绘制poly *后,在地图上平移和缩放时,大小一直保持不变

我希望Apple和Google可以解决这个问题而不是声称这是另一家公司的问题.所有这些都改变了IOS12运行的网页,这些网页已经稳定了多年,并且仍然适用于IOS 9和10个iPad,我一直在测试以确保旧设备可以显示当前的网页.希望这个测试/实验有所帮助.

标签:javascript,webkit,safari,mobile-safari,html5-canvas
来源: https://codeday.me/bug/20191006/1860659.html