javascript - 在将文件写入客户端磁盘时从blob创建/对象URL释放内存

标签 javascript memory-leaks blob revokeobjecturl

更新

由于在询问以下问题并找到代码中的错误后遇到了一个更基本的问题,因此我在MDN Web文档中找到了更多信息,例如downloads API方法downloads.download()指出已撤销对象仅在下载文件/ URL之后才执行URL。因此,我花了一些时间试图了解Web扩展是否使下载API onChanged事件对网页的javascript“可用”,并且认为没有。我不明白为什么downloads API仅适用于扩展,特别是当有很多关于此相同的内存使用/对象URL吊销问题的问题时。例如,Wait for user to finish downloading a blob in Javascript

如果您知道,请您解释一下?谢谢。

从关闭Firefox浏览器开始,然后右键单击本地html文件以在Firefox中打开,它会打开五个firefox.exe进程(在Windows任务管理器中查看)。其中四个进程从20,000k到25,000k的内存开始,而一个进程约有115,000k。

该html页面具有一个indexedDB数据库,其中包含50个对象存储,每个存储包含50个对象。每个对象都从其对象存储中提取出来,并使用JSON.stringify转换为字符串,然后写入二维数组。之后,将数组中的所有元素连接成一个大字符串,转换为blob,然后通过URL对象将其写入硬盘,此后立即将其撤销。最终文件约为190MB。

如果在转换为Blob之前就停止了代码,则firefox.exe进程之一的内存使用量将增加到约425,000k,然后在将数组的元素连接到一个数组后约5-10秒内回落到25,000k。单字符串。

如果代码运行完成,则同一firefox.exe进程的内存使用量将增长到大约1,000,000k,然后下降到大约225,000k。从115,000k开始的firefox.exe进程在代码的blob阶段也会增加到大约325,000k,并且永远不会减少。

在将blob作为文本文件写入磁盘后,这两个firefox.exe进程再也不会释放大约2 x 200,000k的内存增加。

我已经将每个函数中使用的每个变量都设置为null,除非刷新页面,否则永远不会释放内存。同样,此过程是通过按钮单击事件启动的。如果再次运行而没有中间刷新,则这两个firefox.exe进程中的每个进程每次运行都会获取额外的200,000k内存。

我一直无法弄清楚如何释放内存?

这两个功能非常简单。 json [i] [j]保存数据库中第i个对象存储中第j个对象的字符串版本。 os_data []是小型对象数组{“name”:objectStoreName,“count”:n},其中n是商店中对象的数量。如果未调用write_to_disk,则build_text功能似乎会释放内存。因此,问题似乎与Blob或url有关。

我可能忽略了一些显而易见的事情。感谢您提供的任何指导。

编辑:

我从JavaScript: Create and save file看到我的revokeObjectURL(blob)陈述有误。它无法撤消Blob,需要将createObjectURL(blob)保存到类似url的变量中,然后撤消url,而不是blob。

在大多数情况下,这大部分都是有效的,并且内存是从上述两个firefox.exe进程中释放的。这给我留下了一个有关URL撤销时间的小问题。

如果撤销是为了释放内存,那么是否应该仅在成功下载文件之后才撤销URL?如果撤销是在用户单击“确定”下载文件之前发生的,那会发生什么?假设我单击按钮以从数据库准备文件,并且准备好后,浏览器将打开下载窗口,但是我稍等片刻,想着该文件的名称或保存位置,不会撤销声明可以运行,但浏览器仍然保留该url,因为它将下载该内容?我知道我仍然可以下载文件,但是撤销是否仍释放内存?从我对这个示例进行的少量试验来看,在这种情况下似乎没有发布它。

如果有一个事件在文件成功或未成功下载到客户端时触发,那不是应该取消该URL的时间吗?最好在取消URL之前先设置几分钟的超时时间,因为我敢肯定没有事件表明下载到客户端已经结束。

我可能不太了解这方面的基本知识。谢谢。

function build_text() {

    var i, j, l, txt = "";

    for ( i = 1; i <=50; i++ ) {

         l = os_data[i-1].count;

         for  ( j = 1; j <= l; j++ ) {

              txt += json[i][j] + '\n';

         }; // next j

    }; // next i


    write_to_disk('indexedDB portfolio', txt); 

    txt = json = null;

} // close build_text




function write_to_disk( fileName, data ) {  

    fileName = fileName.replace(".",""); 

    var blob = new Blob( [data], { type: 'text/csv' } ), elem;  


    if ( window.navigator.msSaveOrOpenBlob ) {

         window.navigator.msSaveBlob(blob, fileName);

    } else {

        elem = window.document.createElement('a');

        elem.href = window.URL.createObjectURL(blob);

        elem.download = fileName;        

        document.body.appendChild(elem);

        elem.click();        

        document.body.removeChild(elem);

        window.URL.revokeObjectURL(blob);

   }; // end if


   data = blob = elem = fileName = null;


} // close write_to_disk

最佳答案

我对这里的问题有点迷茫...

但是,让我们尝试至少回答一下:

首先,让我们解释URL.createObjectURL(blob)大致做什么:

它创建一个blob URI,这是一个指向内存中Blob blob的URI,就像它位于可访问的位置(例如服务器)一样。
只要未撤消,此blob URI就会将blob标记为无法被垃圾收集器(GC)收集,因此您不必在脚本中维护对blob的实时引用,但是您可以仍然使用/加载它。

然后URL.revokeObjectURL将断开Blob URI和内存中Blob之间的链接。它不会直接释放blob占用的内存,只会删除它对GC的保护,[并且不再指向任何地方]。
因此,如果您有多个指向同一Blob对象的Blob URI,则仅吊销一个不会破坏其他Blob URI。

现在,只有在启动GC时,才释放内存,而内存只有在浏览器内部,认为是最佳时间或没有其他选择(通常是错过了内存空间)时才由浏览器内部决定。 )。

因此,您通常不会看到立即释放内存,这是很正常的,根据经验,我想说FF不关心使用大量内存(如果可用),因此不会经常发生GC踢,这对用户体验很有好处(GCing通常会导致滞后)。

对于您的下载问题,确实,Web API不能提供一种方法来知道下载是成功还是失败,甚至是刚刚结束。
对于撤消部分,这实际上取决于您执行操作的时间。
如果直接在点击处理程序中执行此操作,则浏览器将尚未完成预取请求,因此当发生默认的点击操作(下载)时,URI不会链接任何内容不再。
现在,如果您确实在“保存”提示后撤消了Blob URI,则浏览器将完成一个预取请求,因此可能能够自行标记不应清除Blob资源。但是我认为这种行为不受任何规范的束缚,最好至少等待窗口的focus事件,这时应该已经开始下载资源。

const blob = new Blob(['bar']);
const uri = URL.createObjectURL(blob);
anchor.href = uri;
anchor.onclick = e => {
  window.addEventListener('focus', e=>{
    URL.revokeObjectURL(uri);
    console.log("Blob URI revoked, you won't be able to download it anymore");
  }, {once: true});
};
<a id="anchor" download="foo.txt">download</a>

关于javascript - 在将文件写入客户端磁盘时从blob创建/对象URL释放内存,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59386102/

相关文章:

Java 网络应用程序 : strange memory statistics

c++ - 这种情况是否需要删除或创建新变量?

Angular HttpClient : Property 'headers' does not exist on type 'Blob'

php - 文件类型输入不在循环中

javascript - React - 使用数据设置状态不起作用/将其设置为空数组 - 为什么?

javascript - 用javascript计算2点之间的方位

javascript - 您怎么知道如何初始化脚本?

javascript - 三.JS r71 : Transparent renderer doesn't work with post-processing filters

iOS CGBitmapContextCreate 复制数据吗?

java - 为什么我得到 org.hibernate.HibernateException : IOException occurred reading a binary value