Web Audio API Memory Leak









up vote
1
down vote

favorite












How should a web-audio-api AudioNode be cleaned up so that its memory is released? I am calling oscillatorNode.stop() and oscillatorNode.disconnect() based on this post, but it doesn't seem to help, and I end up with memory leaks. This post doesn't apply because I'm removing the references as soon as I stop the oscillatorNode.



I created a sample website that shows the issue. Here are the steps to reproduce.



  1. Create a local html file and run the below code snippet in chrome on a desktop or laptop with dev tools open.

  2. Create a heap snapshot.

  3. Click the "Go" button.

  4. Periodically create another heap snapshot.

  5. Notice that the memory keeps rising, even after running the garbage collector. Why?

enter image description here



<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>









share|improve this question



















  • 1




    A bug in Chrome? bugs.chromium.org/p/chromium/issues/detail?id=717528
    – James Lawruk
    Nov 12 at 13:47














up vote
1
down vote

favorite












How should a web-audio-api AudioNode be cleaned up so that its memory is released? I am calling oscillatorNode.stop() and oscillatorNode.disconnect() based on this post, but it doesn't seem to help, and I end up with memory leaks. This post doesn't apply because I'm removing the references as soon as I stop the oscillatorNode.



I created a sample website that shows the issue. Here are the steps to reproduce.



  1. Create a local html file and run the below code snippet in chrome on a desktop or laptop with dev tools open.

  2. Create a heap snapshot.

  3. Click the "Go" button.

  4. Periodically create another heap snapshot.

  5. Notice that the memory keeps rising, even after running the garbage collector. Why?

enter image description here



<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>









share|improve this question



















  • 1




    A bug in Chrome? bugs.chromium.org/p/chromium/issues/detail?id=717528
    – James Lawruk
    Nov 12 at 13:47












up vote
1
down vote

favorite









up vote
1
down vote

favorite











How should a web-audio-api AudioNode be cleaned up so that its memory is released? I am calling oscillatorNode.stop() and oscillatorNode.disconnect() based on this post, but it doesn't seem to help, and I end up with memory leaks. This post doesn't apply because I'm removing the references as soon as I stop the oscillatorNode.



I created a sample website that shows the issue. Here are the steps to reproduce.



  1. Create a local html file and run the below code snippet in chrome on a desktop or laptop with dev tools open.

  2. Create a heap snapshot.

  3. Click the "Go" button.

  4. Periodically create another heap snapshot.

  5. Notice that the memory keeps rising, even after running the garbage collector. Why?

enter image description here



<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>









share|improve this question















How should a web-audio-api AudioNode be cleaned up so that its memory is released? I am calling oscillatorNode.stop() and oscillatorNode.disconnect() based on this post, but it doesn't seem to help, and I end up with memory leaks. This post doesn't apply because I'm removing the references as soon as I stop the oscillatorNode.



I created a sample website that shows the issue. Here are the steps to reproduce.



  1. Create a local html file and run the below code snippet in chrome on a desktop or laptop with dev tools open.

  2. Create a heap snapshot.

  3. Click the "Go" button.

  4. Periodically create another heap snapshot.

  5. Notice that the memory keeps rising, even after running the garbage collector. Why?

enter image description here



<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






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Nov 12 at 13:46

























asked Nov 10 at 17:07









TwitchBronBron

1,2471025




1,2471025







  • 1




    A bug in Chrome? bugs.chromium.org/p/chromium/issues/detail?id=717528
    – James Lawruk
    Nov 12 at 13:47












  • 1




    A bug in Chrome? bugs.chromium.org/p/chromium/issues/detail?id=717528
    – James Lawruk
    Nov 12 at 13:47







1




1




A bug in Chrome? bugs.chromium.org/p/chromium/issues/detail?id=717528
– James Lawruk
Nov 12 at 13:47




A bug in Chrome? bugs.chromium.org/p/chromium/issues/detail?id=717528
– James Lawruk
Nov 12 at 13:47












2 Answers
2






active

oldest

votes

















up vote
1
down vote



accepted










According to this post, "When the oscillator stops it should automatically disconnect itself from any downstream nodes". However, due to this bug in chrome (thanks James Lawruk for finding this), it doesn't actually clean itself up.



This comment from that bug report mentions




The issue is that because disconnect() is called right after stop(), the oscillator is disconnected from the destination, so any processing associated with stop() is never done. This also includes not actually stopping the oscillator because it takes at least one render quantum to do that. Since it was disconnected, that render quantum never happens.




So with that in mind, I attached to the oscillatorNode.onended event and call disconnect in that callback, and no more memory leaks!



Here's the code:



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();
);



And the heap snapshots:
enter image description here






share|improve this answer



























    up vote
    1
    down vote













    The comment in the 717528 bug says:




    The issue is that because disconnect() is called right after stop(),
    the oscillator is disconnected from the destination, so any processing
    associated with stop() is never done. This also includes not actually
    stopping the oscillator because it takes at least one render quantum
    to do that. Since it was disconnected, that render quantum never
    happens.




    So if you add a delay before calling disconnect(), it should stay at a consistent memory level.



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







    share|improve this answer




















    • Thanks, this would probably work in most cases, as long as the browser doesn't take more than 100ms to complete the stop operation. I tried a setTimeout of 1ms and that did NOT work.
      – TwitchBronBron
      Nov 12 at 18:19










    Your Answer






    StackExchange.ifUsing("editor", function ()
    StackExchange.using("externalEditor", function ()
    StackExchange.using("snippets", function ()
    StackExchange.snippets.init();
    );
    );
    , "code-snippets");

    StackExchange.ready(function()
    var channelOptions =
    tags: "".split(" "),
    id: "1"
    ;
    initTagRenderer("".split(" "), "".split(" "), channelOptions);

    StackExchange.using("externalEditor", function()
    // Have to fire editor after snippets, if snippets enabled
    if (StackExchange.settings.snippets.snippetsEnabled)
    StackExchange.using("snippets", function()
    createEditor();
    );

    else
    createEditor();

    );

    function createEditor()
    StackExchange.prepareEditor(
    heartbeatType: 'answer',
    convertImagesToLinks: true,
    noModals: true,
    showLowRepImageUploadWarning: true,
    reputationToPostImages: 10,
    bindNavPrevention: true,
    postfix: "",
    imageUploader:
    brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
    contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
    allowUrls: true
    ,
    onDemand: true,
    discardSelector: ".discard-answer"
    ,immediatelyShowMarkdownHelp:true
    );



    );













     

    draft saved


    draft discarded


















    StackExchange.ready(
    function ()
    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53241345%2fweb-audio-api-memory-leak%23new-answer', 'question_page');

    );

    Post as a guest















    Required, but never shown

























    2 Answers
    2






    active

    oldest

    votes








    2 Answers
    2






    active

    oldest

    votes









    active

    oldest

    votes






    active

    oldest

    votes








    up vote
    1
    down vote



    accepted










    According to this post, "When the oscillator stops it should automatically disconnect itself from any downstream nodes". However, due to this bug in chrome (thanks James Lawruk for finding this), it doesn't actually clean itself up.



    This comment from that bug report mentions




    The issue is that because disconnect() is called right after stop(), the oscillator is disconnected from the destination, so any processing associated with stop() is never done. This also includes not actually stopping the oscillator because it takes at least one render quantum to do that. Since it was disconnected, that render quantum never happens.




    So with that in mind, I attached to the oscillatorNode.onended event and call disconnect in that callback, and no more memory leaks!



    Here's the code:



    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();
    );



    And the heap snapshots:
    enter image description here






    share|improve this answer
























      up vote
      1
      down vote



      accepted










      According to this post, "When the oscillator stops it should automatically disconnect itself from any downstream nodes". However, due to this bug in chrome (thanks James Lawruk for finding this), it doesn't actually clean itself up.



      This comment from that bug report mentions




      The issue is that because disconnect() is called right after stop(), the oscillator is disconnected from the destination, so any processing associated with stop() is never done. This also includes not actually stopping the oscillator because it takes at least one render quantum to do that. Since it was disconnected, that render quantum never happens.




      So with that in mind, I attached to the oscillatorNode.onended event and call disconnect in that callback, and no more memory leaks!



      Here's the code:



      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();
      );



      And the heap snapshots:
      enter image description here






      share|improve this answer






















        up vote
        1
        down vote



        accepted







        up vote
        1
        down vote



        accepted






        According to this post, "When the oscillator stops it should automatically disconnect itself from any downstream nodes". However, due to this bug in chrome (thanks James Lawruk for finding this), it doesn't actually clean itself up.



        This comment from that bug report mentions




        The issue is that because disconnect() is called right after stop(), the oscillator is disconnected from the destination, so any processing associated with stop() is never done. This also includes not actually stopping the oscillator because it takes at least one render quantum to do that. Since it was disconnected, that render quantum never happens.




        So with that in mind, I attached to the oscillatorNode.onended event and call disconnect in that callback, and no more memory leaks!



        Here's the code:



        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();
        );



        And the heap snapshots:
        enter image description here






        share|improve this answer












        According to this post, "When the oscillator stops it should automatically disconnect itself from any downstream nodes". However, due to this bug in chrome (thanks James Lawruk for finding this), it doesn't actually clean itself up.



        This comment from that bug report mentions




        The issue is that because disconnect() is called right after stop(), the oscillator is disconnected from the destination, so any processing associated with stop() is never done. This also includes not actually stopping the oscillator because it takes at least one render quantum to do that. Since it was disconnected, that render quantum never happens.




        So with that in mind, I attached to the oscillatorNode.onended event and call disconnect in that callback, and no more memory leaks!



        Here's the code:



        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();
        );



        And the heap snapshots:
        enter image description here







        share|improve this answer












        share|improve this answer



        share|improve this answer










        answered Nov 12 at 13:58









        TwitchBronBron

        1,2471025




        1,2471025






















            up vote
            1
            down vote













            The comment in the 717528 bug says:




            The issue is that because disconnect() is called right after stop(),
            the oscillator is disconnected from the destination, so any processing
            associated with stop() is never done. This also includes not actually
            stopping the oscillator because it takes at least one render quantum
            to do that. Since it was disconnected, that render quantum never
            happens.




            So if you add a delay before calling disconnect(), it should stay at a consistent memory level.



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







            share|improve this answer




















            • Thanks, this would probably work in most cases, as long as the browser doesn't take more than 100ms to complete the stop operation. I tried a setTimeout of 1ms and that did NOT work.
              – TwitchBronBron
              Nov 12 at 18:19














            up vote
            1
            down vote













            The comment in the 717528 bug says:




            The issue is that because disconnect() is called right after stop(),
            the oscillator is disconnected from the destination, so any processing
            associated with stop() is never done. This also includes not actually
            stopping the oscillator because it takes at least one render quantum
            to do that. Since it was disconnected, that render quantum never
            happens.




            So if you add a delay before calling disconnect(), it should stay at a consistent memory level.



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







            share|improve this answer




















            • Thanks, this would probably work in most cases, as long as the browser doesn't take more than 100ms to complete the stop operation. I tried a setTimeout of 1ms and that did NOT work.
              – TwitchBronBron
              Nov 12 at 18:19












            up vote
            1
            down vote










            up vote
            1
            down vote









            The comment in the 717528 bug says:




            The issue is that because disconnect() is called right after stop(),
            the oscillator is disconnected from the destination, so any processing
            associated with stop() is never done. This also includes not actually
            stopping the oscillator because it takes at least one render quantum
            to do that. Since it was disconnected, that render quantum never
            happens.




            So if you add a delay before calling disconnect(), it should stay at a consistent memory level.



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







            share|improve this answer












            The comment in the 717528 bug says:




            The issue is that because disconnect() is called right after stop(),
            the oscillator is disconnected from the destination, so any processing
            associated with stop() is never done. This also includes not actually
            stopping the oscillator because it takes at least one render quantum
            to do that. Since it was disconnected, that render quantum never
            happens.




            So if you add a delay before calling disconnect(), it should stay at a consistent memory level.



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








            share|improve this answer












            share|improve this answer



            share|improve this answer










            answered Nov 12 at 14:02









            James Lawruk

            20.6k14103118




            20.6k14103118











            • Thanks, this would probably work in most cases, as long as the browser doesn't take more than 100ms to complete the stop operation. I tried a setTimeout of 1ms and that did NOT work.
              – TwitchBronBron
              Nov 12 at 18:19
















            • Thanks, this would probably work in most cases, as long as the browser doesn't take more than 100ms to complete the stop operation. I tried a setTimeout of 1ms and that did NOT work.
              – TwitchBronBron
              Nov 12 at 18:19















            Thanks, this would probably work in most cases, as long as the browser doesn't take more than 100ms to complete the stop operation. I tried a setTimeout of 1ms and that did NOT work.
            – TwitchBronBron
            Nov 12 at 18:19




            Thanks, this would probably work in most cases, as long as the browser doesn't take more than 100ms to complete the stop operation. I tried a setTimeout of 1ms and that did NOT work.
            – TwitchBronBron
            Nov 12 at 18:19

















             

            draft saved


            draft discarded















































             


            draft saved


            draft discarded














            StackExchange.ready(
            function ()
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53241345%2fweb-audio-api-memory-leak%23new-answer', 'question_page');

            );

            Post as a guest















            Required, but never shown





















































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown

































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown







            Popular posts from this blog

            Top Tejano songwriter Luis Silva dead of heart attack at 64

            ReactJS Fetched API data displays live - need Data displayed static

            政党