【问题标题】:Web Audio API Memory LeakWeb 音频 API 内存泄漏
【发布时间】:2018-11-10 17:07:19
【问题描述】:

应该如何清理 web-audio-api AudioNode 以释放其内存?我根据this post 调用oscillatorNode.stop()oscillatorNode.disconnect(),但它似乎没有帮助,最终导致内存泄漏。 This post 不适用,因为我会在停止振荡器节点后立即删除引用。

我创建了一个显示问题的示例网站。以下是重现的步骤。

  1. 创建一个本地 html 文件,并在打开开发工具的台式机或笔记本电脑上以 chrome 运行以下代码 sn-p。
  2. 创建堆快照。
  3. 点击“开始”按钮。
  4. 定期创建另一个堆快照。
  5. 请注意,即使在运行垃圾收集器之后,内存也会不断增加。 为什么

<html>
<body>
    <button onclick="go()">Go</button>
    <button onclick="cancel=true">Cancel</button>
    <div id="status"></div>

    <script>
        var cancel = false;
        var statusEl = document.getElementById('status');

        async function go() {
            cancel = false;
            for (var i = 0; i < 100000; i++) {
                if (cancel) {
                    return;
                }
                statusEl.innerHTML = i;
                play();
                await new Promise((resolve) => { setTimeout(resolve, 1); });
                stop();
            }
        }

        var ctx = new AudioContext();
        var data = {
            oscillatorNode: null
        };

        function play() {
            if (!data.oscillatorNode) {
                // create an oscillator
                data.oscillatorNode = ctx.createOscillator();
                data.oscillatorNode.frequency.value = 220.0;
                data.oscillatorNode.connect(ctx.destination);
                data.oscillatorNode.start(ctx.currentTime);
            }
        }

        function stop() {
            if (data.oscillatorNode) {
                data.oscillatorNode.stop();
                data.oscillatorNode.disconnect();
                delete data.oscillatorNode;
            }
        } 
    </script>
</body>

</html>

【问题讨论】:

标签: javascript memory-leaks web-audio-api


【解决方案1】:

根据this post,“当振荡器停止时,它应该自动断开与任何下游节点的连接”。然而,由于to this bug in chrome(感谢James Lawruk 找到这个),它实际上并没有清理自己。

该错误报告中提到的This comment

问题在于,由于在 stop() 之后立即调用了 disconnect(),因此振荡器与目标断开连接,因此与 stop() 相关的任何处理都不会完成。这还包括不实际停止振荡器,因为它需要至少一个渲染量子来做到这一点。由于已断开连接,因此渲染量子永远不会发生。

因此,考虑到这一点,我附加到 oscillatorNode.onended 事件并在该回调中调用 disconnect,并且不再有内存泄漏!

代码如下:

function stop() {
    return new Promise((resolve) => {
        //whenever the node actually finishes playing, disconnect it
        data.oscillatorNode.onended = function () {
            data.oscillatorNode.disconnect();
            delete data.oscillatorNode;
            resolve();
        }
        //stop the oscillator
        data.oscillatorNode.stop();
    });
} 

还有堆快照:

【讨论】:

    【解决方案2】:

    717528 bug中的评论说:

    问题是因为 disconnect() 是在 stop() 之后立即调用的, 振荡器与目的地断开连接,因此任何处理 与 stop() 相关的永远不会完成。这也包括实际上不 停止振荡器,因为它至少需要一个渲染量子 要做到这一点。既然断开了,那渲染量子永远不会 发生。

    因此,如果您在调用 disconnect() 之前添加延迟,它应该保持在一致的内存级别。

    function stop() {
        if (data.oscillatorNode) {
            data.oscillatorNode.stop();
            var oscillatorNode = data.oscillatorNode;
            setTimeout(function() { 
               oscillatorNode.disconnect(); 
               delete oscillatorNode;
            }, 100);    
        }
    } 
    

    【讨论】:

    • 谢谢,这可能在大多数情况下都有效,只要浏览器完成停止操作的时间不超过 100 毫秒。我尝试了 1ms 的 setTimeout,但没有成功。
    猜你喜欢
    • 1970-01-01
    • 2014-07-29
    • 2011-11-06
    • 2023-03-27
    • 2013-03-16
    • 2018-07-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多