views:

57

answers:

2

I just noticed a strange behaviour while looking at my application in the Flash Profiler. When I click a button in my TitleWindow then the TitleWindow doesn't get garbage collected after it is removed. I have no idea why that is happening.

I've created a small example application, so you can try it out yourself:

Main.mxml

<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark"
               xmlns:mx="library://ns.adobe.com/flex/mx" pageTitle="Memory Leak (Spark)">

    <fx:Script>
        <![CDATA[
            protected function openWindowBtn_clickHandler():void
            {
                removeAllElements();
                addElement(new ExampleView());
            }
        ]]>
    </fx:Script>

    <s:controlBarContent>
        <s:Button label="Open Window" id="openWindowBtn" click="openWindowBtn_clickHandler()"/>
    </s:controlBarContent>
</s:Application>

ExampleView.mxml

<?xml version="1.0" encoding="utf-8"?>
<s:TitleWindow xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark"
               xmlns:mx="library://ns.adobe.com/flex/mx" width="100%" height="100%" title="Example View" close="closeHandler()">

    <fx:Script>
        <![CDATA[
            import mx.core.IVisualElementContainer;

            protected function closeHandler():void
            {
                var visualElementParent:IVisualElementContainer = parent as IVisualElementContainer;

                if (visualElementParent)
                    visualElementParent.removeElement(this);
                else
                    parent.removeChild(this);
            }
        ]]>
    </fx:Script>

    <s:layout>
        <s:VerticalLayout verticalAlign="middle" horizontalAlign="center"/>
    </s:layout>

    <s:Button id="doSomethingBtn" label="Click me!"/>
</s:TitleWindow>

When you click "Open Window" and close the ExampleView without clicking the "Click me!" button in it then the GC kicks in and removes the ExampleView. However, when you click on "Click me!" and close the ExampleView afterwards, the ExampleView stays in memory forever.

I wasn't able to find the references in the Profiler which cause this behaviour. I hope someone knows a solution to this, otherwise Flex is creating a lot of memory leaks.

+1  A: 

I may be wrong, but iirc EventListeners added in MXML are always created with a strong reference, which would prevent the Button from being GC'ed.

Have you tried adding the EventListener manually with setting it to being a weak reference? If you look at the list of EventListeners in the Debugger you should see something like a WeakMethodClosure if it was added with a weak reference.

Baelnorn
At first I suspected the same. So, as you can see in the example, I've removed the buttons event handlers. Still, whenever I click the button the TitleWindow won't get garbage collected. Moreover, the button referencing a closure in it's parent shouldn't be a problem.
Gerhard
@gertschi: Ah sorry, misread the first button for the second somehow. <_< What happens to the `closeHandler`? Are the function and the variables properly GC'ed after running? Other than that I can't see anything right now that was still holding a reference. As a last resort you could report the problem and open a bug on [Adobe's bugtracker](http://bugs.adobe.com/flex/).
Baelnorn
Thanks for hinting at the closeHandler. I took a closer look and discovered that removing the TitleWindow from the TitleWindow's close event (or any other event that is fired from within the window) doesn't work without creating a memory leak when the "Click me" button has been clicked. Removing the TitleWindow by clicking an additional button in the applications ControlBar always let's the GC remove it from memory as well.
Gerhard
It definitly has something to do with the containers child components like the Button or a TextInput. I created a bug on Adobe's bugtracker: [#SDK-28259](http://bugs.adobe.com/jira/browse/SDK-28259).
Gerhard
+1  A: 

One thing you're probably forgetting is that garbage collection isn't collecting unreferenced objects at the moment they loose the last reference. Usually the GC will collect the loose instances only after you create some object, but even than it's not obvious if it will do it in that moment. You can read more about it here:

About garbage collection

Or take a look at this presentation: Garbage Collection - Alex Harui


<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark"
           xmlns:mx="library://ns.adobe.com/flex/mx" pageTitle="Memory Leak (Spark)">

<fx:Script>
    <![CDATA[
        protected function openWindowBtn_clickHandler():void
        {
            removeAllElements();
            addElement(new ExampleView());
        }

        protected function button1_clickHandler(event:MouseEvent):void
        {
            var o:Object = new Object();
            System.gc();
        }

    ]]>
</fx:Script>

<s:controlBarContent>
    <s:Button label="Open Window" id="openWindowBtn" click="openWindowBtn_clickHandler()"/>
    <s:Button label="Force GC"  click="button1_clickHandler(event)"/>
</s:controlBarContent>
</s:Application>

Take a look at this. If you press the "Force GC" button a couple of times, it will collect the ExampleWindow. In a real world application that does something this happens without the need to call the System.gc() (in fact, it's not a good practice to call it), but after a while, so the things don't just disapear when you're done with them, they disapear when you're done, and Flash Player decides it needs to clean up.

Robert Bak
Thanks for the links. I've already known those. Just to make sure, I took another look at them. However, I don't think that the GC is ignoring those instances even though they are unreferenced. Obviously the components within the TitleWindow (e.g. by clicking on the "Click me" button) can prevent the TitleWindows' garbage collection. I'm just not able to figure out why this is happening.
Gerhard
Of course you are right. Flash Player's GC collects only when it is necessary. However, I'm convinced that there's a bug somewhere in there. Just take a look at the bug [#SDK-28259](http://bugs.adobe.com/jira/browse/SDK-28259) I created. There's a simple example that shows how a child component like a TextInput can prevent the garbage collection of its parent. I just tested your way of forcing the GC in it and it didn't help. Anyway, I'll mark this as answer since you provided a bunch of useful information/links and hope that my bug report doesn't get as neglected by Adobe as many others are.
Gerhard