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