JavaScript中的内存泄漏(WebWorker、Canvas、IndexedDB)
我需要一些帮助来查找浏览器/WebWorker JavaScript中的内存泄漏。我在这段代码中找到了它:JavaScript中的内存泄漏(WebWorker、Canvas、IndexedDB),javascript,memory-leaks,html5-canvas,indexeddb,offscreen-canvas,Javascript,Memory Leaks,Html5 Canvas,Indexeddb,Offscreen Canvas,我需要一些帮助来查找浏览器/WebWorker JavaScript中的内存泄漏。我在这段代码中找到了它: /** * Resizes an Image * * @function scaleImage * @param {object} oImageBlob blob of image * @param {int} iHeight New height of Image * @return {ImageBi
/**
* Resizes an Image
*
* @function scaleImage
* @param {object} oImageBlob blob of image
* @param {int} iHeight New height of Image
* @return {ImageBitmap} ImageBitmap Object
*/
async function scaleImage(oImageBlob, iHeight) {
var img = await self.createImageBitmap(oImageBlob);
var iWidth = Math.round( ( img.width / img.height ) * iHeight);
var canvas = new OffscreenCanvas(iWidth,iHeight);
var ctx = canvas.getContext('2d');
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
return(canvas.transferToImageBitmap());
}
它的名字是:
[inside a web worker: Some looping that calls this about 1200 times while parsind files from a s3 bucket ...]
var oImageBlob = await new Response(oS3Object.Body, {}).blob();
var oThumbnail = await scaleImage(oImageBlob, 100);
await IDBputData(oInput.sFileKey, oImageBlob, oInput.sStore, oThumbnail)
[... end of the loop]
另一个内部功能是
/**
* Put information to IndexedDB
*
* @function IDBputData
* @param {string} sKey key of information
* @param {string} sValue information to upload
* @param {string} sStore name of object store
* @param {object} oThumbnail optrional, default: null, thumbnail image
* @return {object} - SUCCESS: array, IndexedDB Identifyer "key"
* - FAIL: Error Message
*/
async function IDBputData(sKey, sValue, sStore, oThumbnail=null) {
var oGeneratedKeys = {};
if(sStore=="panelStore"){
oGeneratedKeys = await getKeyfromSKey(sKey);
}
return new Promise((resolve, reject) => {
const tx = oConn.transaction(sStore, 'readwrite');
const store = tx.objectStore(sStore);
var request = {}
request = store.put({panelkey: oGeneratedKeys.panelkey, layerkey: oGeneratedKeys.layerkey, countrycode: oGeneratedKeys.countrycode, value: sValue, LastModified: new Date(), oThumbnail: oThumbnail});
request.onsuccess = () => (oThumbnail.close(),resolve(request.result));
request.onerror = () => (oThumbnail.close(),reject(request.error));
});
}
当我在Chrome中以这种方式运行它时,它将消耗我所有空闲内存(大约8GB),然后崩溃。(带CPU/GPU共享RAM的笔记本电脑)
当我改变
var oThumbnail = await scaleImage(oImageBlob, 100);
到
Chrome的RAM消耗保持在800MB左右,因此必须有最顶级的函数“scaleImage”
我试着调整它,但没有成功
/**
* Resizes an Image
*
* @function scaleImage
* @param {object} oImageBlob blob of image
* @param {int} iHeight New height of Image
* @return {ImageBitmap} ImageBitmap Object
*/
async function scaleImage(oImageBlob, iHeight) {
var img = await self.createImageBitmap(oImageBlob);
var iWidth = Math.round( ( img.width / img.height ) * iHeight);
var canvas = new OffscreenCanvas(iWidth,iHeight);
var ctx = canvas.getContext('2d');
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
var oImageBitmap = canvas.transferToImageBitmap();
ctx = null;
canvas = null;
iWidth = null;
img = null;
return(oImageBitmap);
}
非常感谢您的帮助。要使ImageBitmap以最有效的方式发布其位图数据,您必须在完成后调用其方法 但实际上,您不需要这个
scaleImage
函数。有一个resizeHeight
选项,如果您在使用该选项时没有使用resizeWidth
选项,则可以通过保持纵横比来精确调整图像大小,就像您在函数中所做的那样,只是不需要指定位图两次
一旦调整了此图像位图的大小,就可以将其传输到(将在内部close()
原始图像位图),并从该渲染器调用transferToBlob()
)。
这对你的电脑来说应该更轻一些
async function worker\u script(){
const blob=等待获取(“https://upload.wikimedia.org/wikipedia/commons/4/47/PNG_transparency_demonstration_1.png)然后((resp)=>resp.blob());
//直接从createImageBitmap调整大小
const img=wait createImageBitmap(blob,{resizeHeight:100});
const canvas=新的偏置画布(img.width,img.height);
getContext(“位图渲染器”)
.transferFromImageBitmap(img);//这会在内部关闭ImageBitmap
const resized_blob=await canvas.convertToBlob();
//putinib(调整大小的\u blob);
//对于演示,我们将该Blob传递给main,但不必这样做
//改为显示画布;)
postMessage({blob:resized_blob,width:img.width});
//为了安全起见,我们甚至可以将画布调整为0x0以完全释放其位图
canvas.width=canvas.height=0;
}
//回到主站
const-worker=新工作者(getWorkerURL());
worker.onmessage=({data:{blob,width}})=>{
const img=新图像();
img.src=URL.createObjectURL(blob);
img.onload=(evt)=>URL.revokeObjectURL(img.src);
文件.正文.附加(img);
log(“ImageBitmap是否已分离?”,宽度==0);
};
函数getWorkerURL(){
const content=“!”+worker_script.toString()+“();”;
constblob=newblob([content],{type:“text/javascript”});
返回URL.createObjectURL(blob);
}
当使用store.put创建的请求解决时,您应该清理图像位图。示例request.onsuccess=()=>(oThumbnail.close(),resolve(request.result))
(在onerror
中执行相同操作)调用ImageBitmap.close()
将处理位图使用的资源。@Blindman67听起来不错,但在几分钟前对其进行测试后,我可以说:没有任何区别。:-(我相应地更新了问题中的代码。但我肯定还遗漏了一些东西。:-(你有没有清理过你不断创建的屏幕外画布var canvas=new OffscreenCanvas(iWidth,iHeight)
?@根据我的研究canvas=null;应该可以。我在问题的最后一个代码段中尝试了这个方法。你认为我做错了什么吗?@模糊画布的作用域是函数,因此在函数返回时被取消引用。谢谢。我同意你的解决方案,但是,我有两点意见:->创建eImageBitmap()和resizeHeight都可以工作。但是,即使质量“高”,它也会导致明显的低质量。另外,我对几百个全高清文件的测试表明,在Chrome上80%的情况下,它的速度与Chrome一样快,甚至更慢。(不,我也没想到。:-)->Blindman67和Underfuse首先帮助我找到了正确的解决方案(参见问题的评论),因此,如果他们中的一个想发布一个公平的答案,我会再等一天。你的测试结果真的很奇怪……通过这条路,我肯定做得更少(我上个月在Chrome的实现中修复了一个不相关的bug)。如果我有时间,我会自己设置一个测试,看看发生了什么,但是首先,你能再次检查一下你的系统上是否启用了所有硬件加速吗(chrome://gpu/)。阅读这里的评论,只有Blindman67给了你正确的建议。如果有帮助,我可以尝试分享有关我的测试设置的详细信息。“图形功能状态”下的所有内容都已启用或硬件加速,尽管“Vulkan”我的印象是,不将可选参数传递给createImageBitmap可能会跳过比canvas构造函数慢一些的代码。我不知道chorme的实现端,尽管这只是一个猜测。运行一个非常快速的测试(),在我的计算机上,bitmaprender的性能比2D环境好很多:对于100张原始Blob的完整副本的图像,15秒比21秒(我最终得到的是13秒比19秒)。关于质量,在createImageBitmap中使用resizeQuality“high”进行缩小时,确实出现了一些问题……在“low”和“Middle”之间有更大的区别在“低”和“高”之间…drawImage似乎与“高”和“中”oO绘制了相同的图形
/**
* Resizes an Image
*
* @function scaleImage
* @param {object} oImageBlob blob of image
* @param {int} iHeight New height of Image
* @return {ImageBitmap} ImageBitmap Object
*/
async function scaleImage(oImageBlob, iHeight) {
var img = await self.createImageBitmap(oImageBlob);
var iWidth = Math.round( ( img.width / img.height ) * iHeight);
var canvas = new OffscreenCanvas(iWidth,iHeight);
var ctx = canvas.getContext('2d');
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
var oImageBitmap = canvas.transferToImageBitmap();
ctx = null;
canvas = null;
iWidth = null;
img = null;
return(oImageBitmap);
}