(This is a bit simplified version of what I’m doing, but basicaly that’s it)
Problem is, memory does not get released and after enough songs are loaded (and later removed), the browser crashes with “out of memory” message. Is there something obvious I’m missing?
Is it normal that when I load MP3 song, around 100MB of memory gets allocated?
I’m using Window’s Resource Monitor to check what’s going on in memory. Are there any better tools?
Sorry for bumping my own thread, but there is really something fishy going on. Here is a very simplified stand-alone version of what I’m doing in my player:
I have an array of sounds which I load and play one by one every 5 seconds.
The sounds are played on two sound channels which I toggle (first sound plays on first channel, then second sound on second channel, third sound again on first channel and so on… second channel, first channel, second, first…).
After I play 5 sounds, I remove all event listeners, close the sounds, null them, stop both channels, null them and empty the channel array.
But still - there is aprox. 1GB (!?!!) of memory used which doesn’t get garbage collected.
What am I doing wrong?
Important notices:
i’m exporting to HTML5 and using Chrome
You must use “Allow-Control-Allow-Origin” plugin for Chrome to avoid the “No ‘Access-Control-Allow-Origin’ header is present on the requested resource.” error. Or use some other MP3 urls. (Very interesting: if “No ‘Access-Control-Allow-Origin’ header is present on the requested resource.” error comes up, there are NO memory issues, but that’s not how things work in my real-life player).
You can see my test case below.
var mp3Urls:Array<String> = new Array<String>();
var sounds:Array<Sound> = new Array<Sound>();
var soundsLoaded:Int = 0;
var finished:Bool = false;
var newTimer:Timer;
var soundChannel:Array<SoundChannel> = new Array<SoundChannel>();
var activeChannel:Int = 0;
function loadNextSound() {
activeChannel++;
if (activeChannel > 1)
activeChannel = 0;
var newSound = new Sound();
newSound.addEventListener(Event.COMPLETE, onSoundLoadComplete, false, 0, true);
newSound.load(new URLRequest(mp3Urls[soundsLoaded]));
sounds.push(newSound);
}
function onSoundLoadComplete(e:Event) {
if (soundChannel[activeChannel] != null) {
soundChannel[activeChannel].stop();
}
if (sounds.length > 0) {
soundChannel[activeChannel] = sounds[sounds.length - 1].play();
}
trace("sounds loaded: " + sounds.length);
newTimer = new Timer(5000);
newTimer.addEventListener(TimerEvent.TIMER, playerLoop, false, 0, true);
newTimer.start();
}
function onSoundPlayComplete(e:Event) {
}
function deleteSounds() {
trace("stoping channels");
for (i in 0...soundChannel.length) {
if (soundChannel[i] != null) {
soundChannel[i].stop();
soundChannel[i] = null;
}
}
trace("deleting sounds");
for (i in 0...sounds.length) {
sounds[i].removeEventListener(Event.COMPLETE, onSoundLoadComplete, false);
sounds[i].close();
sounds[i] = null;
trace("deleted index: " + i);
}
while (sounds.length > 0)
sounds.pop();
trace("sounds remaining: " + sounds.length);
trace("sounds deleted");
while (soundChannel.length > 0)
soundChannel.pop();
soundChannel = null;
trace("channels remmoved");
}
function playerLoop(e:Event) {
if (newTimer != null)
newTimer.removeEventListener(TimerEvent.TIMER, playerLoop);
if (soundsLoaded < 5 && !finished) {
soundsLoaded++;
loadNextSound();
}
else {
finished = true;
deleteSounds();
}
}
public static function main() {
mp3Urls[0] = "http://www.mfiles.co.uk/mp3-downloads/brahms-cello-sonata-Em-1.mp3";
mp3Urls[1] = "http://www.mfiles.co.uk/mp3-downloads/frederic-chopin-piano-sonata-2-op35-3-funeral-march.mp3";
mp3Urls[2] = "http://www.mfiles.co.uk/mp3-downloads/saint-saens-carnival-of-the-animals-the-swan.mp3";
mp3Urls[3] = "http://www.mfiles.co.uk/mp3-downloads/frederic-chopin-nocturne-no20.mp3";
mp3Urls[4] = "http://www.mfiles.co.uk/mp3-downloads/mozart-clarinet-concerto-2.mp3";
mp3Urls[5] = "http://www.mfiles.co.uk/mp3-downloads/schubert-trout-quintet-4.mp3";
mp3Urls[6] = "http://www.mfiles.co.uk/mp3-downloads/brahms-cello-sonata-Em-1.mp3";
loadNextSound();
return;
}
I think I’ve figured it out. There is an empty dispose method in HTML5AudioSource.hx. Is there a reason for that?
I’ve added the howlers unload() call to that method and now memory is being cleaned up just fine.
Can someone predict any side effects?
Here’s the mentioned method in HTML5AudioSource.hx:
public function dispose ():Void {
#if howlerjs
parent.buffer.__srcHowl.unload();
#end
}
You are right, after some additional testing I also noticed some ugly side effects.
But I still think the howler’s unload() method should be called somewhere. If this method is not called memory quickly gets stuffed with howler’s cache… 1GB after streaming 10 songs(?!)
Maybe in sound.close() ?
I’m testing this right now and looks good so far.
public function dispose ():Void {
#if lime_console
if (channels > 0) {
src.release ();
channels = 0;
}
#end
#if howlerjs
__srcHowl.unload();
#end
}