robotlegs / robotlegs-framework

An ActionScript 3 application framework for Flash and Flex
https://robotlegs.tenderapp.com/
MIT License
967 stars 261 forks source link

Robotlegs is stuck in memory and won't let go #164

Closed ziibibi closed 9 years ago

ziibibi commented 10 years ago

Creating almost empty instance of RL2 and later removing all references to it won't clear memory. To reproduce behavior do this:

package {
    import flash.display.Sprite;
    import flash.display.Stage;
    import flash.events.Event;
    import flash.events.KeyboardEvent;
    import robotlegs.bender.bundles.mvcs.MVCSBundle;
    import robotlegs.bender.extensions.contextView.ContextView;
    import robotlegs.bender.framework.impl.Context;
    public class Main extends Sprite {      
        private var gameContainer:Sprite;
        private var context:Context;
        public function Main():void {
            if (stage) init();
            else addEventListener(Event.ADDED_TO_STAGE, init);
        }
        private function init(e:Event = null):void {
            removeEventListener(Event.ADDED_TO_STAGE, init);
            // entry point
            stage.addEventListener(KeyboardEvent.KEY_UP, destroy);
            gameContainer = new Sprite();
            addChild(gameContainer);
            context = new Context();
            context.install(MVCSBundle);
            context.configure(new ContextView(gameContainer));
        }
        private function destroy(e:KeyboardEvent):void {
            stage.removeEventListener(KeyboardEvent.KEY_UP, destroy);
            removeChild(gameContainer);
            gameContainer = null;
            context = null;
        }
    }
}

As follows from example above clean instance of RL2 is created after that, as you can see, event listener to KeyboardEvent.KEY_UP initiates shutdown of RL2 by removing view from stage, which by means of StageSyncExtension launches context.destroy() and hence everything should be gone - but that is not what happens. Instead Context instance is stuck in memory and GC will never delete it. If sample is right, and sample is so primitive that chances that I am erring in here are quite slim, then, as I can see, we have only 2 ways out of this - either Flash player is having bug with GC or some of extensions withing MVCSBundle are not destroying themselves properly.

Actually this is driving me nuts, please someone explain how this can be! ;)

darscan commented 10 years ago

Hello,

won't clear memory

How have you tested for this? Are you running a debug SWF, forcing GC, using a profiler etc?

ziibibi commented 10 years ago

Environment is latest FlashDevelop with flash player 11.4 as a target. Leftovers are to be seen in memory consumption, which does not decrease (no matter how it's displayed from profiler or direct trace of System class property) and in profiler class listing (which shows decrease of some but definitely not all class instances). And yes, I am forcing GC.

darscan commented 10 years ago

Thanks, I'll take a look.

darscan commented 10 years ago

Hello. Sorry for the delays - I don't have much free time at the moment.

I did some analysis using Scout, and there's definitely memory being leaked. It looks quite bad at first (especially when running a debug SWF in the debug player).

I see RL2 pinning around 618 KB of "used" memory after destroying a single context (release SWF in a debug player). It's a little hard to explain right now, but not all of that is because of "leaked" instances. That number rises to around 822 KB after creating and destroying over 25 contexts. So, we might be looking at around 8 KB of leakage per context ((822 - 618) / 25). I'll try to look into this in greater detail soon.

ziibibi commented 10 years ago

I just run my own test like this and I see increase from 5kb with no contexts at all to 12kb after creating a first context followed by slow increase up to 15kb for following 22 contexts. So, yes this does not seem so bad. Unless those leaked instances contain references to application classes which are kept as well hence potential memory loss can be way bigger. So for some reason Dictionaries in swiftsuspenders seem to stay. robotlegs.bender.extensions.viewManager.impl::ContainerRegistry also keep some references to DisplayObjectContainer's. For me this arose from fact that as I try to destroy my application by simply destroying context and removing main container and that just not working. Hence I'm trying to make sure I have clean entry point here.

darscan commented 10 years ago

Hi @ziibibi

There's a new version of RL2 (v2.2.1) that might reduce the GC issue a bit (includes fixes to both RL and Swiftsuspenders). Could you download the latest from http://www.robotlegs.org/ and have another look?

Something I noticed is that the logger creates a lot of unique Strings. These strings end up in the VM's constant pool (as far as I'm aware) and can end up taking up a lot of memory.

It might be worth having a look at what happens when you use a custom Bundle (instead of MVCS) that doesn't include the logging extensions.