Spark Combox 内存泄漏

发布于 2024-10-17 13:57:23 字数 2279 浏览 3 评论 0原文

我有一个与 Spark Combo 框相关的简单、可重现的内存泄漏,但我确信这一定是我做错了,而不是 SDK 错误。

// Application.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">
    <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>

当针对 Flex 4.1 进行编译时,这似乎会造成内存泄漏,其中 ComboBoxView 永远不会被 GC 回收——因为对 ComboBox 的引用存在延迟。

这是探查器的输出: 在此处输入图像描述

重现步骤:

  • 使用 Application.mxml创建项目ComboBoxView.mxml
  • 使用 Flex 4.1 编译项目
  • 使用探查器启动 Application.mxml
  • 创建内存快照
  • 单击按钮将视图添加到舞台
  • 再次单击按钮从舞台删除视图
  • 运行垃圾收集器
  • 创建另一个内存快照
  • 查看两个内存快照之间的延迟对象

注意 - 当应用程序针对 Flex Hero 进行编译时,不会发生这种情况。

看起来好像这是一个错误,但我不敢相信 ComboBox 有内存泄漏——这肯定会在 4.1 发布之前就被修复了吗?

我在这里做错了什么?为什么视图没有被 GC 处理?

更新 我对此进行了进一步调查,并认为问题在于 ComboBoxSkin 使用的 RichEditableText 组件的问题。请参阅此处的详细信息: Spark 内存泄漏

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: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:
enter image description here

Steps to reproduce:

  • Create a project with Application.mxml and ComboBoxView.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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

清欢 2024-10-24 13:57:23

MXML 有时很棘手,因为您永远不知道幕后到底发生了什么(除非您使用 -keep 属性并查看)。

但是,在我看来,您永远不会取消 ComboBox 引用。删除容器的子级与从内存中删除该实例不同。例如,在 Flextras 日历组件中,随着月份的变化,我们从显示中删除日期的情况并不少见。根据月份的不同,显示的天数可能在 28 到 31 之间。但是,如果您所在的月份有 31 天,然后切换到有 30 天的月份,那么我们不会取消那些额外的天渲染器,我们只是将它们缓存在“unusedDays”数组中,然后我们将它们准备好用于下一个月份切换的时间。

ListBase 类做类似的事情,我相信 Flex 4 甚至有一个名为 useVirtualLayout 的属性来控制这些东西如何保存在内存中。

我希望这不是一个太冗长或自我放纵的解释,我希望它是有道理的。

我认为你需要做一些事情来清空组合框。我要尝试的第一件事是给 ComboBox 一个 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>

然后在 ActionScript 代码中创建 ComboBoxView 的实例(不是本地函数):

protected var vew:ComboBoxView = new ComboBoxView();

然后像这样处理垃圾收集:

        if (hasElement)
        {
            button.setFocus();
            comboBoxContainer.removeAllElements();
            comboBoxContainer.myComboBox = null;
            comboBoxContainer = null;
            hasElement = false;
        } else {
            var vew:ComboBoxView = new ComboBoxView();
            comboBoxContainer.addElement(vew);
            hasElement = true;
        }
    }

我自己没有进行测试,我的直觉是,并非所有这些步骤都是必需的;但我怀疑我的方法可能是正确的。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文