Fetch API 在 Chrome 中泄漏内存

Posted

技术标签:

【中文标题】Fetch API 在 Chrome 中泄漏内存【英文标题】:Fetch API leaks memory in Chrome 【发布时间】:2019-03-12 18:52:37 【问题描述】:

当以最简单的方式使用 fetch-API 时,Chrome 无法正确收集垃圾。我做错了吗?

for (i = 0; i < 100; i++) 
  fetch('https://upload.wikimedia.org/wikipedia/commons/3/3d/LARGE_elevation.jpg')
    .then(response => 
      console.log('Memory-bloating')
    )

https://jsfiddle.net/dozrpcvj/12/

这个 JSFiddle 用 1.4GB 填充内存,直到您手动进行垃圾收集或关闭选项卡后才会释放。如果将迭代次数增加到 1000 次,它会“下载”14GB(从自己的磁盘),而不是垃圾收集,而是开始填充磁盘上的交换文件。

是我做错了什么还是 Chrome 中的错误?使用 Safari 进行测试时,它也会用 1.4GB 填充硬盘驱动器,但一旦完成就会开始垃圾收集。

PS。您不能使用内存分析器,因为它告诉您您只使用了几 MB 的数据,即使活动监视器或 Chrome 自己的任务管理器显示 1.4GB。

【问题讨论】:

有趣的注释 - 我似乎无法在开发工具中找到该内存。拍摄内存快照报告约 10 兆,查看性能选项卡,它不会报告异常的内存使用 - 从约 8 兆到最多约 16,在加载期间。任务管理器确实显示了超过 1.5 gig 的已用内存,并且我打开了一个 Chrome 选项卡。在性能选项卡上点击记录,然后进行垃圾回收会释放大约一兆的 JS 堆内存(11MB -> 10MB),但任务管理器报告至少释放了一个内存。 @vlaz 是的,这也是我注意到的。我已经通过 Chrome 向 Google 报告了它,但我想知道是我使用了错误的方式获取,还是 Chrome 处理了错误的方式。 不管怎样,我在 Firefox 中得到了类似的结果。有时。 JSFiddle 将我的内存使用量从 1.2GB 增加到 ~3.3GB,但随后它就清理干净了。但是,在控制台中执行相同的代码会达到 2.7GB 左右,并且会在此停留一段时间,然后才会自行清理。看起来 GC 有时会稍后启动。有时我似乎与 Chrome 有类似的结果:打开新选项卡 -> 在控制台中运行代码。 第一次我这样做,它达到〜1.6GB,然后又回到〜250MB。 第二次 我在同一个选项卡中执行此操作(不重新加载),内存保持高位。 我尝试了很多方法,包括将 null 设置为变量、调整数组大小和手动触发 GC。最后,我已经解决了刷新页面。这会立即降低内存使用量,但会导致延迟,直到我可以在浏览器中执行更多 javascript Chrome 正在尽其所能,您没有做错任何事。内存的分配是设计使然。 Chrome 会自动每隔一段时间收集垃圾,然后内存将再次被释放。由您(如果您按下检查器内的回收站)或automatically in ("unknown") intervals。如果您将每个响应引用存储在(例如)window["fetch-n"] 中,则内存分配将在垃圾回收后保留。 【参考方案1】:

您没有做错任何事情,因为这是调用 fetch 的正确方法:

https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch

这也不是 Chrome 中的错误。它的行为符合预期: 对于每次 fetch,它都会创建一个对象,该对象将在所有 Promise 完成过程中更新。所有 http 标头(已发送、已接收),实际的图像数据,随叫随到。您调用它 100 次,因此每个承诺 14 Mb。最终,promise 将完成,垃圾收集器将释放使用的内存。我检查了小提琴,这需要一些时间(几分钟),但最终内存会被释放。

也许如果您解释一下为什么以及如何需要同时启动 100 个 http 调用,就有办法做到这一点,这样浏览器就不会保留 1.4 GB 的 RAM。 “Promise.all”或“Promise.race”在内存使用方面可能更有效。

【讨论】:

我们在 React 中使用它来实现我们何时应该向我们的用户抛出“有一个新版本”提示。我们每 5 分钟执行一次请求,几周后没有新版本,打开的选项卡占用了一些内存。也许它现在已经修复了,但那时它从来没有被垃圾回收过。【参考方案2】:

你没有做错任何事;只是当你同时调用一堆 Promise 时,会占用大量内存。 如果你想确保它不使用太多内存——并且你愿意让它花更长的时间——你可以把这一切都放在一个异步函数中并这样做:

for (i = 0; i < 100; i++) 
  await fetch('https://upload.wikimedia.org/wikipedia/commons/3/3d/LARGE_elevation.jpg')
  console.log('Memory-bloating')

【讨论】:

【参考方案3】:
fetch('https://upload.wikimedia.org/wikipedia/commons/3/3d/LARGE_elevation.jpg')
    .then(response => response.json())
    .then(data => console.log(data))

【讨论】:

这是如何解决/解释问题的?

以上是关于Fetch API 在 Chrome 中泄漏内存的主要内容,如果未能解决你的问题,请参考以下文章

Android开发常见的Activity中内存泄漏及解决办法

分析 ThreadLocal 内存泄漏问题

如何在长时间运行的 Perl 程序中找到内存泄漏?

内存泄漏和内存溢出的区别

内存泄漏与垃圾回收机制

OpenGL VBO 会泄漏内存吗?