组件不会被垃圾收集
我刚注意到一个奇怪的行为,同时在Flash Profiler中查看我的应用程序。当我在TitleWindow中单击一个按钮时,TitleWindow在删除后不会被垃圾收集。我不知道为什么会这样。组件不会被垃圾收集
我创建了一个小示例应用程序,所以你可以尝试一下自己:
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>
当您单击“打开窗口”并关闭示例视图,而不点击“点击我!”按钮,然后GC启动并删除ExampleView。但是,当您点击“点击我!”时然后关闭ExampleView,ExampleView永远留在内存中。
我无法在Profiler中找到导致此行为的引用。我希望有人知道这个解决方案,否则Flex会造成大量内存泄漏。
你可能会忘记的一件事是,垃圾收集没有收集未引用的对象,他们在最后一次引用丢失的时刻。通常情况下,GC只会在创建一个对象后才会收集这些松散的实例,但即使它不是很明显是否会在那个时候执行。你可以阅读更多关于它在这里:
还是先看看本演示: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>
看看这个。如果您按下“强制GC”按钮几次,它将收集ExampleWindow。在一个现实世界的应用程序中,这样做会发生这种情况,而不需要调用System.gc()(实际上,调用它不是一个好习惯),但是过了一段时间,所以这些事情不会随着时间流逝而消失与他们一起完成,完成后他们会消失,并且Flash Player决定需要清理。
感谢您的链接。我已经知道这些。为了确保,我再看看他们。但是,我认为GC并不会忽略这些实例,即使它们没有被引用。很明显,TitleWindow中的组件(例如,通过点击“Click me”按钮)可以防止TitleWindows的垃圾收集。我只是无法弄清楚为什么会发生这种情况。 – 2010-10-18 13:26:58
当然你是对的。 Flash Player的GC仅在需要时收集。但是,我确信在那里有一个错误。只要看看我创建的bug [#SDK-28259](http://bugs.adobe.com/jira/browse/SDK-28259)。有一个简单的例子,显示了像TextInput这样的子组件如何阻止其父项的垃圾回收。我刚刚测试了强制GC的方式,但没有帮助。无论如何,我会将此标记为答案,因为您提供了一系列有用的信息/链接,并希望我的错误报告不会像其他许多人那样被Adobe忽视。 – 2010-10-20 07:07:35
Adobe的bug追踪程序已关闭。但是,现在可以在[#FLEX-25652](https://issues.apache.org/jira/browse/FLEX-25652)的Apaches bugtracker中找到该问题。 – 2013-03-15 19:51:53
我可能错了,但MXML中添加的iirc EventListeners总是带有强引用,这会阻止Button被GC化。
您是否尝试过手动添加EventListener并将其设置为弱引用?如果您查看调试器中的EventListeners列表,您应该看到类似于WeakMethodClosure
的内容,如果它添加了弱引用。
起初我怀疑是一样的。所以,如您在示例中所看到的,我已经删除了按钮事件处理程序。不过,只要我点击按钮TitleWindow不会被垃圾收集。而且,引用它父项中的闭包的按钮应该不成问题。 – 2010-10-18 11:31:19
@gertschi:对不起,误解了第二个第一个按钮。 <_ class="text-secondary"> Baelnorn 2010-10-18 11:45:17
感谢暗示在closeHandler。我仔细看了看,发现从TitleWindow的关闭事件(或从窗口内部触发的任何其他事件)中删除TitleWindow不起作用,而在单击“Click me”按钮时不会产生内存泄漏。通过单击应用程序ControlBar中的附加按钮来移除TitleWindow总是让GC从内存中移除它。 – 2010-10-18 13:13:56
看起来像ExampleView没有收集垃圾,因为点击“Click Me”时会添加一些EventListener。 避免这种情况,最好的办法是 1.添加事件侦听createComplete事件手动 2.是closeHandler 3.删除事件侦听从容器中取出的按钮,将其设置为空
现在ExampleView会乱收垃圾
进一步测试后,它看起来像这样只发生一次。因此,如果您执行三个步骤(打开窗口,单击“单击我”,关闭窗口)三次,只有一个TitleWindow实例不会被垃圾收集。 – 2010-10-18 11:45:48