JavaScript 中的内存泄漏(WebWorker、Canvas、IndexedDB)
memory leak in JavaScript (WebWorker, Canvas, IndexedDB)
我需要一些帮助来查找小型浏览器/WebWorker 中的内存泄漏 JavaScript。我追踪到这段代码:
/**
* 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 中以这种方式 运行 时,它会消耗我可用的所有 RAM(大约 8 GB),然后崩溃。 (具有 CPU/GPU 共享 RAM 的笔记本电脑)。
当我改变
var oThumbnail = await scaleImage(oImageBlob, 100);
至
var oThumbnail = null;
Chrome 的 RAM 消耗固定在 800 MB 左右,因此必须有一些具有最顶层功能“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 以最有效的方式释放其位图数据,您必须在完成后调用其 .close()
方法。
但实际上,您并不需要这个scaleImage
功能。 createImageBitmap()
有一个 resizeHeight
选项,如果你在没有 resizeWidth
的情况下使用它,你将通过保持纵横比精确来调整图像大小,就像你在你的函数中所做的那样,除了它不需要两次分配位图。
调整大小后的 ImageBitmap 后,您可以将其传输到 BitmapRenderingContext(这将在内部 close()
原始 ImageBitmap)并从该渲染器调用 transferToBlob()
。
这对您的计算机来说应该更轻。
async function worker_script() {
const blob = await fetch( "https://upload.wikimedia.org/wikipedia/commons/4/47/PNG_transparency_demonstration_1.png" ).then( (resp) => resp.blob() );
// resize from createImageBitmap directly
const img = await createImageBitmap( blob , { resizeHeight: 100 } );
const canvas = new OffscreenCanvas( img.width, img.height );
canvas.getContext( "bitmaprenderer" )
.transferFromImageBitmap( img ); // this internally closes the ImageBitmap
const resized_blob = await canvas.convertToBlob();
// putInIDB( resized_blob );
// for the demo we pass that Blob to main, but one shouldn't have to do that
// show the canvas instead ;)
postMessage( { blob: resized_blob, width: img.width } );
// to be safe, we can even resize our canvas to 0x0 to free its bitmap entirely
canvas.width = canvas.height = 0;
}
// back to main
const worker = new Worker( getWorkerURL() );
worker.onmessage = ({ data: { blob, width } }) => {
const img = new Image();
img.src = URL.createObjectURL( blob );
img.onload = (evt) => URL.revokeObjectURL( img.src );
document.body.append( img );
console.log( "ImageBitmap got detached?", width === 0 );
};
function getWorkerURL() {
const content = "!" + worker_script.toString() + "();";
const blob = new Blob( [ content ], { type: "text/javascript" } );
return URL.createObjectURL( blob );
}
我需要一些帮助来查找小型浏览器/WebWorker 中的内存泄漏 JavaScript。我追踪到这段代码:
/**
* 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 中以这种方式 运行 时,它会消耗我可用的所有 RAM(大约 8 GB),然后崩溃。 (具有 CPU/GPU 共享 RAM 的笔记本电脑)。
当我改变
var oThumbnail = await scaleImage(oImageBlob, 100);
至
var oThumbnail = null;
Chrome 的 RAM 消耗固定在 800 MB 左右,因此必须有一些具有最顶层功能“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 以最有效的方式释放其位图数据,您必须在完成后调用其 .close()
方法。
但实际上,您并不需要这个scaleImage
功能。 createImageBitmap()
有一个 resizeHeight
选项,如果你在没有 resizeWidth
的情况下使用它,你将通过保持纵横比精确来调整图像大小,就像你在你的函数中所做的那样,除了它不需要两次分配位图。
调整大小后的 ImageBitmap 后,您可以将其传输到 BitmapRenderingContext(这将在内部 close()
原始 ImageBitmap)并从该渲染器调用 transferToBlob()
。
这对您的计算机来说应该更轻。
async function worker_script() {
const blob = await fetch( "https://upload.wikimedia.org/wikipedia/commons/4/47/PNG_transparency_demonstration_1.png" ).then( (resp) => resp.blob() );
// resize from createImageBitmap directly
const img = await createImageBitmap( blob , { resizeHeight: 100 } );
const canvas = new OffscreenCanvas( img.width, img.height );
canvas.getContext( "bitmaprenderer" )
.transferFromImageBitmap( img ); // this internally closes the ImageBitmap
const resized_blob = await canvas.convertToBlob();
// putInIDB( resized_blob );
// for the demo we pass that Blob to main, but one shouldn't have to do that
// show the canvas instead ;)
postMessage( { blob: resized_blob, width: img.width } );
// to be safe, we can even resize our canvas to 0x0 to free its bitmap entirely
canvas.width = canvas.height = 0;
}
// back to main
const worker = new Worker( getWorkerURL() );
worker.onmessage = ({ data: { blob, width } }) => {
const img = new Image();
img.src = URL.createObjectURL( blob );
img.onload = (evt) => URL.revokeObjectURL( img.src );
document.body.append( img );
console.log( "ImageBitmap got detached?", width === 0 );
};
function getWorkerURL() {
const content = "!" + worker_script.toString() + "();";
const blob = new Blob( [ content ], { type: "text/javascript" } );
return URL.createObjectURL( blob );
}