Web Audio API Memory Leak

Multi tool use
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.
- Create a local html file and run the below code snippet in chrome on a desktop or laptop with dev tools open.
- Create a heap snapshot.
- Click the "Go" button.
- Periodically create another heap snapshot.
- Notice that the memory keeps rising, even after running the garbage collector. Why?
<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
add a comment |
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.
- Create a local html file and run the below code snippet in chrome on a desktop or laptop with dev tools open.
- Create a heap snapshot.
- Click the "Go" button.
- Periodically create another heap snapshot.
- Notice that the memory keeps rising, even after running the garbage collector. Why?
<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
A bug in Chrome? bugs.chromium.org/p/chromium/issues/detail?id=717528
– James Lawruk
Nov 12 at 13:47
add a comment |
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.
- Create a local html file and run the below code snippet in chrome on a desktop or laptop with dev tools open.
- Create a heap snapshot.
- Click the "Go" button.
- Periodically create another heap snapshot.
- Notice that the memory keeps rising, even after running the garbage collector. Why?
<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
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.
- Create a local html file and run the below code snippet in chrome on a desktop or laptop with dev tools open.
- Create a heap snapshot.
- Click the "Go" button.
- Periodically create another heap snapshot.
- Notice that the memory keeps rising, even after running the garbage collector. Why?
<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
javascript memory-leaks web-audio-api
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
add a comment |
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
add a comment |
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:
add a comment |
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);
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
add a comment |
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:
add a comment |
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:
add a comment |
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:
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:
answered Nov 12 at 13:58
TwitchBronBron
1,2471025
1,2471025
add a comment |
add a comment |
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);
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
add a comment |
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);
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
add a comment |
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);
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);
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
add a comment |
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
add a comment |
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
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
Ayv9V7lv528dwnQlNcJdHAt,nRMzDgRE
1
A bug in Chrome? bugs.chromium.org/p/chromium/issues/detail?id=717528
– James Lawruk
Nov 12 at 13:47