Компонентът не се събира за боклук

Току-що забелязах странно поведение, докато разглеждах приложението си във 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>

Когато щракнете върху "Отвори прозорец" и затворете ExampleView, без да щракнете върху "Щракнете върху мен!" бутон в него, тогава GC се включва и премахва ExampleView. Въпреки това, когато щракнете върху "Щракнете ме!" и затворете ExampleView след това, ExampleView остава в паметта завинаги.

Не успях да намеря препратките в Profiler, които причиняват това поведение. Надявам се някой да знае решение за това, в противен случай Flex създава много течове на памет.


person Gerhard Schlager    schedule 18.10.2010    source източник
comment
След допълнителни тестове изглежда, че това се случва само веднъж. Така че, ако изпълните стъпките (отворете прозорец, щракнете върху Щракнете върху мен, затворете прозореца) три пъти, само едно копие на TitleWindow не получава събиране на боклука.   -  person Gerhard Schlager    schedule 18.10.2010


Отговори (3)


Едно нещо, което вероятно забравяте е, че събирането на боклук не събира нереферирани обекти в момента, в който те загубят последната референция. Обикновено GC ще събере свободните екземпляри само след като създадете някакъв обект, но дори и тогава не е очевидно дали ще го направи в този момент. Можете да прочетете повече за това тук:

Относно събирането на отпадъци

Или погледнете тази презентация: Събиране на боклука – Алекс Харуи


<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>

Погледни това. Ако натиснете бутона "Force GC" няколко пъти, той ще събере ExampleWindow. В реално приложение, което прави нещо, това се случва, без да е необходимо да се извиква System.gc() (всъщност не е добра практика да го извиквате), но след известно време, така че нещата да не изчезват просто, когато сте приключили с тях, те изчезват, когато сте готови, и Flash Player решава, че трябва да изчисти.

person Robert Bak    schedule 18.10.2010
comment
Благодаря за линковете. Вече ги познавам. За да се уверя, ги погледнах още веднъж. Въпреки това не мисля, че GC пренебрегва тези случаи, въпреки че те не са реферирани. Очевидно компонентите в рамките на TitleWindow (напр. чрез щракване върху бутона Click me) могат да предотвратят събирането на боклука на TitleWindows. Просто не мога да разбера защо се случва това. - person Gerhard Schlager; 18.10.2010
comment
Разбира се, че си прав. GC на Flash Player събира само когато е необходимо. Въпреки това съм убеден, че някъде там има грешка. Просто погледнете програмната грешка #SDK-28259, която създадох. Има прост пример, който показва как дъщерен компонент като TextInput може да предотврати събирането на боклука на своя родител. Току-що тествах вашия начин за форсиране на GC в него и не помогна. Както и да е, ще маркирам това като отговор, тъй като предоставихте куп полезна информация/връзки и се надявам, че моят доклад за грешка няма да бъде пренебрегнат от Adobe, както много други. - person Gerhard Schlager; 20.10.2010
comment
Програмата за проследяване на грешки на Adobe е затворена. Проблемът обаче вече може да бъде открит в инструмента за проследяване на грешки на Apaches на адрес #FLEX-25652 . - person Gerhard Schlager; 15.03.2013

Може да греша, но iirc EventListeners, добавени в MXML, винаги се създават със силна препратка, което би попречило на бутона да бъде GC'ed.

Опитахте ли да добавите EventListener ръчно, като го настроите да бъде слаба референция? Ако погледнете списъка с EventListeners в Debugger, трябва да видите нещо като WeakMethodClosure, ако е добавено със слаба препратка.

person Baelnorn    schedule 18.10.2010
comment
Първоначално се усъмних в същото. И така, както можете да видите в примера, премахнах манипулаторите на събития на бутоните. И все пак, когато щракна върху бутона, TitleWindow няма да се събира боклук. Освен това бутонът, препращащ към затваряне в неговия родител, не би трябвало да е проблем. - person Gerhard Schlager; 18.10.2010
comment
@gertschi: Ах, съжалявам, прочетох погрешно първия бутон за втория по някакъв начин. ‹_‹ Какво се случва с closeHandler? Функцията и променливите правилно ли са GC'd след изпълнение? Освен това в момента не мога да видя нищо, което все още да съдържа препратка. В краен случай можете да съобщите за проблема и да отворите грешка в програмата за проследяване на грешки на Adobe. - person Baelnorn; 18.10.2010
comment
Благодаря за намека за closeHandler. Погледнах по-отблизо и открих, че премахването на TitleWindow от затварящото събитие на TitleWindow (или всяко друго събитие, което се задейства от прозореца) не работи, без да създаде изтичане на памет, когато е щракнат върху бутона Click me. Премахването на TitleWindow чрез щракване върху допълнителен бутон в контролната лента на приложенията винаги позволява на GC да го премахне и от паметта. - person Gerhard Schlager; 18.10.2010
comment
Определено има нещо общо с дъщерните компоненти на контейнерите като бутона или TextInput. Създадох грешка в програмата за проследяване на грешки на Adobe: #SDK-28259. - person Gerhard Schlager; 19.10.2010

Изглежда, че ExampleView не събира Garbage, защото по някакъв начин се добавя някакъв EventListener, когато се щракне върху „Click Me“. Най-добрият начин да избегнете това е да 1. добавите Event Listener ръчно в събитие createComplete 2. Премахнете EventListener в closeHandler 3. Премахнете бутона от контейнера и го задайте на null

Сега ExampleView ще бъде събран боклук

person user5073283    schedule 02.07.2015