Spark Combox Memory Leak
I have a simple, reproducable memory leak associated with the Spark Combo box, however I'm convinced it must be something I'm doing wrong, rather than an SDK bug.
// Application.mxml
<?xml version="1.0" encoding="utf-8"?>
<s开发者_StackOverflow社区: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">
<s:layout>
<s:VerticalLayout />
</s:layout>
<fx:Script>
<![CDATA[
import mx.events.FlexEvent;
private var hasElement:Boolean;
protected function toggleContainer():void
{
if (hasElement)
{
button.setFocus();
comboBoxContainer.removeAllElements();
hasElement = false;
} else {
var vew:ComboBoxView = new ComboBoxView();
comboBoxContainer.addElement(vew);
hasElement = true;
}
}
]]>
</fx:Script>
<s:Button id="button" label="Add container" click="toggleContainer()" />
<s:Group id="comboBoxContainer" />
</s:Application>
// ComboBoxView.mxml
<?xml version="1.0" encoding="utf-8"?>
<s:VGroup xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
>
<s:ComboBox />
</s:VGroup>
When compiled against Flex 4.1, this appears to create a memory leak, where ComboBoxView is never GC'd -- because of a lingering reference to the ComboBox.
Here's the output from the profiler:
Steps to reproduce:
- Create a project with
Application.mxml
andComboBoxView.mxml
- Compile the project with Flex 4.1
- Launch Application.mxml with the profiler
- Create a memory snapshot
- Click the button to add the view to the stage
- Click the button again to remvoe the view from the stage
- Run the Garbage Collector
- Create another memory snapshot
- View lingering objects between the two memory snapshots
Note - this doesn't occur when the application is compiled against Flex Hero.
It appears as though this is a bug, but I cannot believe that the ComboBox has a memory leak -- surely this would've been fixed before 4.1 was shipped?
What am I doing wrong here? Why isn't the view being GC'd?
Update I've done further investigation into this, and believe the issue lies in a problem with the RichEditableText component that the ComboBoxSkin uses. See details here: Spark memory leaks
http://www.iampj.com/search/label/combobox%20memory%20leak
I have been having similar problems in flex 3
MXML is tricky sometimes because you never know exactly what is going on behind the scenes (unless you use the -keep attribute and look).
But, it looks to me like you never null out the ComboBox reference. Removing a child of a container is not the same as removing that instance from memory. In the Flextras Calendar component, for example, it is not uncommon for us to remove days from display as months changes. Depending on the month there may be anywhere between 28 and 31 days displayed. But, if you're on a month with 31 days and switch to one with 30 days then we don't null out those extra day renderers, we just cache them in an "unusedDays" array and then we have them ready for the next time the month switches.
The ListBase classes do similar, and I believe Flex 4 even has a property named useVirtualLayout to control how this stuff is kept in memory.
I hope that wasn't a too long winded, or self indulgent, explanation and I hope it made sense.
I think you need to do something to null the ComboBox. The first thing I would try is to give the ComboBox an ID:
<?xml version="1.0" encoding="utf-8"?>
<s:VGroup xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
>
<s:ComboBox id="myComboBox" />
</s:VGroup>
Then in your ActionScript code, create an instance of the ComboBoxView (not function local):
protected var vew:ComboBoxView = new ComboBoxView();
And then deal with the garbage collection like this:
if (hasElement)
{
button.setFocus();
comboBoxContainer.removeAllElements();
comboBoxContainer.myComboBox = null;
comboBoxContainer = null;
hasElement = false;
} else {
var vew:ComboBoxView = new ComboBoxView();
comboBoxContainer.addElement(vew);
hasElement = true;
}
}
I didn't do testing myself, and my intuition is that not all these steps are needed; but I suspect my approach is probably correct.
精彩评论