为什么 Chrome 在内存中保留一个匿名的 MediaRecorder 对象?

Why does Chrome retain an anonymous MediaRecorder object in memory?

在试图理解为什么我的一些 Web 应用程序的对象被 Chrome 保留在内存中之后,我想我可能已经将其缩小到 MediaRecorder 对象被浏览器保留的情况即使应用程序放弃了对它的所有引用,无论是显式的还是隐式的。

请注意以下重现该问题的最小示例:

new MediaRecorder(new MediaStream())

在评估了上面的表达式后——创建了一个没有引用(变量或属性)的 MediaRecorder 对象——我使用 [=51= 中的内存选项卡分析了堆使用情况] 和 Chromium,并且 MediaRecorder 对象在堆上的存在不是 t运行sient——只要我想在堆上找到它,该对象就会一直存在。它不会消失。

为什么要保留?用 Object 之类的普通 class 进行测试——例如,评估 new Object() 的行为符合预期——垃圾收集最终会收集匿名对象。但 MediaRecorder 并非如此。这闻起来很像虫子。

相比之下,Firefox 在适当的时候释放了 MediaRecorder 对象,当然考虑到垃圾收集,至少在我执行上面的语句后它甚至不在堆上秒,而不是当我拍下它的快照。

媒体记录器对象没有被任何东西引用,它应该有最短的生命周期,但它在我清除控制台后拍摄的快照中存在于内存中(开发工具背后的开发人员 Chrome 建议在拍摄堆快照之前清除控制台,因为前者可能会保留原本会被释放的对象。

我在 MediaRecorder class 中找不到任何方法可以指示我可以将它与流分离或以其他方式“关闭”它。除了确保没有明显或不那么明显(例如通过事件侦听器)对它的引用外,我只能希望不保留匿名对象,并且通常不会保留此类对象,但是 MediaRecorder 对象似乎是。似乎没有任何杠杆可以让我拉动来处理一个,可以这么说。

您可以在下面的屏幕截图中看到,保留媒体记录器的对象并不完全是我脚本的一部分,它们似乎与某些内部浏览器状态有关:

“构造函数”列中所选对象旁边的 window 图标有工具提示“用户对象 [是] 可从 window 访问”。上面的代码片段是我在选项卡中 运行 的唯一代码,为什么该对象“可从 window 访问”,如果是,它肯定不是我管理的任何引用?

那么为什么要保留对象呢?这里更大的问题是,如果我的应用程序启动许多记录并为每个记录创建一个新的媒体记录器对象,这些对象将不断堆积在内存中,这是内存泄漏的实际情况。

就像我说的,我 运行 在 Firefox 62.0.2 中使用了相同的语句,并且行为如我所料——我创建的单个 MediaRecorder 对象似乎超出了范围(因为它应该是它不应该引用它的方式)在它被创建后不久。

(Chrome 版本 69.0.3497.100,Windows 10 上的 x64)

确保您在浏览器设置中取消选中 "Preserve log upon navigation",否则 console.clear() 不会执行任何操作。

https://developer.mozilla.org/en-US/docs/Web/API/Console/clear

编辑:

此外,我怀疑这是内存泄漏,因为无法访问的对象会被自动删除。 MediaRecorder 更有可能将对象保存到全局范围。

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Memory_Management#Garbage_collection

“4.3.1 生命周期和媒体流”应该会把事情搞清楚一点。

https://www.w3.org/TR/mediacapture-streams/#stream-api

根据 specification:

A MediaStream object is said to be active when it has at least one MediaStreamTrack that has not ended. A MediaStream that does not have any tracks or only has tracks that are ended is inactive.

当您创建一个新的 MediaStream 时,除非您停止其所有轨道,否则它将保持 活动 状态。

因此,要取消设置此对象,您需要显式删除其 MediaStreamTrack 对象,获取对流的引用,例如:

var myRecorder;
navigator.mediaDevices.getUserMedia({ 
  video: true,
  audio: true
}).then(function create_media_recorder(stream) {
  myRecorder = new MediaRecorder(stream);
  console.clear();
});

var myStream = myRecorder.stream;
myStream.getTracks().forEach(function(el) {
    el.stop()
}

之后,您应该会看到录制图标消失 - 媒体流现在处于非活动状态。

检查这些 API 以供进一步参考

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

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

这已被证实是 Chromium 的一个问题(暗示 Google Chrome 并且 可能 其他 Chromium 衍生物,包括 Electron ):

https://bugs.chromium.org/p/chromium/issues/detail?id=899722

在撰写本文时,该问题被标记为“可用”,我认为这表明它已得到确认并有效地等待某人介入并修复。