2

更新

由于在代码中发现错误后提出了以下问题并得出了一个更基本的问题,因此我发现了更多信息,例如在 MDN Web 文档中的下载 API 方法 downloads.download() 它声明对象的撤销url 应仅文件/url 下载后执行。因此,我花了一些时间试图了解 Web 扩展是否使下载 API onChanged 事件对网页的 javascript“可用”,但我认为它不会。我不明白为什么下载 API 仅可用于扩展,尤其是当有很多关于同一内存使用/对象 URL 撤销问题的问题时。例如,等待用户完成 Javascript 中的 blob 下载

如果你知道,请解释一下好吗?谢谢你。


从关闭 Firefox 浏览器开始,然后右键单击要在 Firefox 中打开的本地 html 文件,它会打开五个 firefox.exe 进程,如在 Windows 任务管理器中所见。其中四个进程的内存在 20,000k 到 25,000k 之间,一个进程的内存约为 115,000k。

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

如果代码在转换为 blob 之前停止,则其中一个 firefox.exe 进程的内存使用量会增加到大约 425,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
4

1 回答 1

7

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

但让我们试着回答,至少是其中的一部分:

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

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

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

现在,只有在 GC 启动时才会释放内存,这仅由浏览器内部决定,当它认为这是最好的时间,或者当它看到它没有其他选项时(通常是当它错过内存空间时)。

所以你看不到你的内存被立即释放是很正常的,根据经验,我会说 FF 不关心使用大量内存,当它可用时,让 GC 踢不那么频繁, whihc 有利于用户体验(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>

于 2018-06-05T10:40:19.033 回答