ENTER_FRAME listener on class appears to keep class allocated in memory
Consider the following code:
import 'package:stagexl/stagexl.dart';
class TestEnterFrame extends Sprite
{
Sprite _bg;
TestEnterFrame()
{
_bg = new Sprite()
..graphics.rect(-400, 0, 600, 100)
..graphics.fillColor(Color.Green)
..useHandCursor = true
..addTo(this);
this.addEventListener(Event.ENTER_FRAME, _enterFrame);
}
void _enterFrame(Event event)
{
print('entered frame');
return;
}
void dispose()
{
print(this.hasEventListener(Event.ENTER_FRAME)); // true
print('removing event listener');
this.removeEventListener(Event.ENTER_FRAME, _enterFrame);
print(this.hasEventListener(Event.ENTER_FRAME)); // false
this.removeChildren();
}
}
TestEnterFrame _test;
void main() {
...
...
...
_test = new TestEnterFrame()
..addTo(stage);
_test.addEventListener(MouseEvent.CLICK, _interact);
}
_interact(Event event)
{
_test.removeEventListener(MouseEvent.CLICK, _interact);
_test
..dispose()
..removeFromParent();
_test = null;
}
Once the TestEnterFrame sprite has been clicked and _interact has fired, I try running a manual garbage collection, and the TestEnterFrame class remains allocated in memory.
However, it appears to be properly released from allocation if I instead attach the ENTER_FRAME listener to the _bg sprite of the class.
Any information would be greatly appreciated!
Hi, this may be because the ENTER_FRAME event is a special kind of event listener. Every time you register such an event listener, the event listener is not only registered in EventDispatcher (base class of DisplayObject) but also in a global list of event listeners. This is necessary to call all ENTER_FRAME listeners efficently, otherwise we would need to iterate over all DisplayObjects in the display list to find those who have such an event listener registered.
The CLICK event listener is different. It is only registered in the EventDispatcher (base class of DisplayObject).
However, if you call removeEventListener for the ENTER_FRAME event listener it should be removed from the global list too. And therefore should be garbage collected.
I need to take a closer look at this, please give me 2-3 days to get back to you.
Okay i played a little bit with your example. When you say that the TestEnterFrame object is not released, do you mean that you still see it allocated in memory? Did you test this with the developer tools of the browser?
The example works as expected right? It's just the thing with the memory. Probably it is just the way how the garbage collector works. Even if you set the _test variable to null, it does not mean that the garbage collector immediately removes the object from memory. If there is no pressure to release allocated memory, the garbage collector will do nothing. Only after a while the GC will kick in an release all objects that are not referenced any more.
Please let me know if this helps or if i miss something here.
I am forcing garbage collection through Dartium's observatory by clicking the GC button in Allocation Profile. When the enter_frame listener is not on this, but rather on the _bg sprite, the allocated instances of TestEnterFrame correctly goes down to 0 after a GC/refresh. If the enter_frame listener is added to this, the allocated instances of TestEnterFrame remains at 1 after a GC/refresh.
I will try that and keep you posted!