2010-10-18 76 views
3

我刚注意到一个奇怪的行为,同时在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会造成大量内存泄漏。

+0

进一步测试后,它看起来像这样只发生一次。因此,如果您执行三个步骤(打开窗口,单击“单击我”,关闭窗口)三次,只有一个TitleWindow实例不会被垃圾收集。 – 2010-10-18 11:45:48

回答

1

你可能会忘记的一件事是,垃圾收集没有收集未引用的对象,他们在最后一次引用丢失的时刻。通常情况下,GC只会在创建一个对象后才会收集这些松散的实例,但即使它不是很明显是否会在那个时候执行。你可以阅读更多关于它在这里:

About garbage collection

还是先看看本演示: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决定需要清理。

+0

感谢您的链接。我已经知道这些。为了确保,我再看看他们。但是,我认为GC并不会忽略这些实例,即使它们没有被引用。很明显,TitleWindow中的组件(例如,通过点击“Click me”按钮)可以防止TitleWindows的垃圾收集。我只是无法弄清楚为什么会发生这种情况。 – 2010-10-18 13:26:58

+0

当然你是对的。 Flash Player的GC仅在需要时收集。但是,我确信在那里有一个错误。只要看看我创建的bug [#SDK-28259](http://bugs.adobe.com/jira/browse/SDK-28259)。有一个简单的例子,显示了像TextInput这样的子组件如何阻止其父项的垃圾回收。我刚刚测试了强制GC的方式,但没有帮助。无论如何,我会将此标记为答案,因为您提供了一系列有用的信息/链接,并希望我的错误报告不会像其他许多人那样被Adobe忽视。 – 2010-10-20 07:07:35

+0

Adob​​e的bug追踪程序已关闭。但是,现在可以在[#FLEX-25652](https://issues.apache.org/jira/browse/FLEX-25652)的Apaches bugtracker中找到该问题。 – 2013-03-15 19:51:53

2

我可能错了,但MXML中添加的iirc EventListeners总是带有强引用,这会阻止Button被GC化。

您是否尝试过手动添加EventListener并将其设置为弱引用?如果您查看调试器中的EventListeners列表,您应该看到类似于WeakMethodClosure的内容,如果它添加了弱引用。

+0

起初我怀疑是一样的。所以,如您在示例中所看到的,我已经删除了按钮事件处理程序。不过,只要我点击按钮TitleWindow不会被垃圾收集。而且,引用它父项中的闭包的按钮应该不成问题。 – 2010-10-18 11:31:19

+0

@gertschi:对不起,误解了第二个第一个按钮。 <_ Baelnorn 2010-10-18 11:45:17

+0

感谢暗示在closeHandler。我仔细看了看,发现从TitleWindow的关闭事件(或从窗口内部触发的任何其他事件)中删除TitleWindow不起作用,而在单击“Click me”按钮时不会产生内存泄漏。通过单击应用程序ControlBar中的附加按钮来移除TitleWindow总是让GC从内存中移除它。 – 2010-10-18 13:13:56

0

看起来像ExampleView没有收集垃圾,因为点击“Click Me”时会添加一些EventListener。 避免这种情况,最好的办法是 1.添加事件侦听createComplete事件手动 2.是closeHandler 3.删除事件侦听从容器中取出的按钮,将其设置为空

现在ExampleView会乱收垃圾