Не удается найти значительную утечку памяти в веб-приложении Flex 4.6 - PullRequest
0 голосов
/ 16 марта 2012

У меня веб-игра Flex 4.6 , которая отображает 2 списка с виртуальными макетами с 2 пользовательскими средствами визуализации элементов. Средства визуализации состоят в основном из BitmapImages, отображающих аватары пользователей + несколько меток.

enter image description here

Списки часто обновляются через сокет TCP с сжатыми данными JSON. Я объединяю эти данные в 2 ArrayCollections , служащих в качестве провайдеров данных для списков. Кажется, это работает хорошо, списки не мерцают и корректно обновляются (я много отслеживал трассировку отладки, чтобы сделать это правильно).

{
    lobby: [
        "OK252342810632",
        "OK122471020773",
        "DE54",
        "DE6577",
        "DE7981",
        "OK225312168135",
        "OK20629248715",
        "DE7880",
    ],
    games: [
        { 0: [] },
        { 9012: [
            "VK48058967",
            "MR14315189992643135976",
            "OK10218913103",
        ] },
        { 9013: [
            "OK305894249541",
            "OK151358069597",
            "OK515549948434",
        ] },
        { 9007: [
            "OK366541092321",
            "DE7062",
            "OK122700338897",
        ] },
        { 8993: [
            "OK78476527766",
            "VK5692120"
        ] }
    ]
}

Моя проблема в том, что приложение очень медленно работает, и для некоторых пользователей происходит сбой плагина, поэтому пользователи часто жалуются.

В профилях я вижу эту картинку (память перепрыгивает до 20 МБ и остается там или иногда переходит к 40 МБ и т. Д.):

enter image description here

Из профилировщика я не могу понять - что утечка памяти. Вверху я вижу Vector. <*> Class - что бы это ни значило.

Я уже столько раз пытался решить эту проблему:

  • Переключен с нативного JSON.parse () обратно на com.brokenfunction.json.decodeJson () (потому что я подумал, что, возможно, Adobe неправильно понял)

    • Я удаляю объект JSON после его анализа:

    закрытая функция handleTcpData (событие: ProgressEvent): void { // читать ByteArray через сокет, распаковать его - работает хорошо

    var obj:Object = decodeJson(_bytes.toString());
    
    // merge into 2 ArrayCollections, update GUI... works well?
    
    for (var key:String in obj)
        delete obj[key];
    obj = null;
    

    }

    • Я отключил ContentCache - без разницы

    • Я перестал использовать любой темп. Объекты типа {id: "DE22"} и непосредственное использование объекта JSON

У кого-нибудь есть хороший совет?

Я читал много документов / блогов по сборке мусора в AS3 и обычные советы: установите для ссылок на объекты значение null, используйте слабые прослушиватели событий, removeChild () не освобождает память и т. Д. - но я не вижу как применить это к моей проблеме.

Ниже мой полный Lobby.mxml исходный код:

<?xml version="1.0" encoding="utf-8"?>
<s:Group 
    xmlns:fx="http://ns.adobe.com/mxml/2009" 
    xmlns:s="library://ns.adobe.com/flex/spark" 
    xmlns:mx="library://ns.adobe.com/flex/mx" 
    xmlns:comps="*"
    width="700" height="450">

    <fx:Declarations>
        <s:RadioButtonGroup id="_filter" change="handleRadio(event);" />
    </fx:Declarations>

    <fx:Metadata> 
        [Event(name="game", type="PrefEvent")] 
    </fx:Metadata>

    <fx:Script>
        <![CDATA[
            import mx.collections.ArrayCollection;
            import mx.utils.ObjectUtil;

            [Bindable]
            private var _games:ArrayCollection = new ArrayCollection();
            [Bindable]
            private var _users:ArrayCollection = new ArrayCollection();

            private function handleRadio(event:Event):void {
                switch (_filter.selection) {
                    case _allBtn:
                        _games.filterFunction = null; 
                        break;
                    case _vacBtn:
                        _games.filterFunction = vacantGame;
                        break;
                    case _fullBtn:
                        _games.filterFunction = fullGame;
                        break;
                }
                _games.refresh();
            }

            private function vacantGame(obj:Object):Boolean {
                for (var id:String in obj) {
                    var players:Array = obj[id];
                    return players.length < 3;
                }

                return false;
            }

            private function fullGame(obj:Object):Boolean {
                for (var id:String in obj) {
                    var players:Array = obj[id];
                    return players.length == 3;
                }

                return false;
            }

            // merge arrays of objects
            private function mergeObjs(ac:ArrayCollection, array:Array):void {
                var i:int;
                var j:int;
                var n:int;
                var src:Array = ac.source;

                // 1) remove items missing in data from _data
                FOUND1:
                for (i = src.length - 1; i >= 0; i--) {
                    for (j = array.length - 1; j >= 0; j--) {
                        if (ObjectUtil.compare(src[i], array[j]) == 0)
                            continue FOUND1;
                    }

                    n = ac.getItemIndex(src[i]);
                    if (Util.DEBUG)
                        trace('REMOVED OBJ ' + src[i] + ' filtered n=' + n + ' i=' + i);

                    // remove visible items
                    if (n > -1)
                        ac.removeItemAt(n);
                    // remove hidden (filtered) items
                    else
                        src.splice(i, 1);
                }


                // 3) add items appeared in data to _data
                FOUND2:
                for (j = 0; j < array.length; j++) {
                    for (i = 0; i < src.length; i++) {
                        if (ObjectUtil.compare(src[i], array[j]) == 0)
                            continue FOUND2;
                    }

                    if (Util.DEBUG) 
                        trace('ADDED OBJ ' + array[j] + ' i=' + i);

                    //ac.addItemAt(array[j], i);
                    ac.addItem(array[j]);
                }
            }

            // merge arrays of strings (the user ids)
            private function mergeIds(ac:ArrayCollection, array:Array):void {
                var i:int;
                var j:int;
                var n:int;
                var src:Array = ac.source;

                // 1) remove items missing in data from _data
                FOUND1:
                for (i = src.length - 1; i >= 0; i--) {
                    for (j = array.length - 1; j >= 0; j--) {
                        if (i == j)
                            continue FOUND1;
                    }

                    n = ac.getItemIndex(src[i]);
                    if (Util.DEBUG)
                        trace('REMOVED ID ' + src[i] + ' filtered n=' + n + ' i=' + i);

                    // remove visible items
                    if (n > -1)
                        ac.removeItemAt(n);
                        // remove hidden (filtered) items
                    else
                        src.splice(i, 1);
                }


                // 3) add items appeared in data to _data
                FOUND2:
                for (j = 0; j < array.length; j++) {
                    for (i = 0; i < src.length; i++) {
                        if (i == j)
                            continue FOUND2;
                    }

                    if (Util.DEBUG) 
                        trace('ADDED ID ' + array[j] + ' i=' + i);

                    ac.addItem(array[j]);
                }
            }

            public function update(games:Array, lobby:Array):void {
                var vac:uint = 0;
                var full:uint = 0;

                for each (var game:Object in games) {
                    for (var id:String in game) {
                        var players:Array = game[id];
                        if (!players)
                            continue;

                        if (players.length < 3)
                            vac++;
                        else if (players.length == 3)
                            full++;
                    }
                }

                if (games)
                    mergeObjs(_games, games);

                if (lobby)
                    mergeIds(_users, lobby);    

                _allBtn.label  = 'All ' + _games.source.length;
                _vacBtn.label  = 'Vacant ' + vac;
                _fullBtn.label = 'Full ' + full;
            }

            public function appendText(str:String):void {
                _chat.appendText(str);
            }
        ]]>
    </fx:Script>

    <s:VGroup width="100%" height="100%">
        <s:HGroup width="100%" verticalAlign="baseline" paddingLeft="8" paddingRight="8">
            <s:Label text="Игровые столы:" />
            <s:RadioButton id="_allBtn" group="{_filter}" label="Все" selected="true" />
            <s:RadioButton id="_vacBtn" group="{_filter}" label="Свободные" />
            <s:RadioButton id="_fullBtn" group="{_filter}" label="Полные" />
            <s:Spacer width="100%" />
            <s:Label text="Игроки в лобби: {_users.length}" />
        </s:HGroup>

        <mx:HDividedBox width="100%" height="100%">
            <s:List id="_gamesList" itemRenderer="Game" useVirtualLayout="true" dataProvider="{_games}" skinClass="PrefListSkin" width="100%" height="100%" minWidth="180">
                <s:layout>
                    <s:TileLayout />
                </s:layout>
            </s:List>

            <mx:VDividedBox width="180" height="100%" minWidth="180">
                <s:List id="_usersList" itemRenderer="User" useVirtualLayout="true" dataProvider="{_users}" skinClass="PrefListSkin" width="100%" height="100%" minHeight="150">
                    <s:layout>
                        <s:TileLayout />
                    </s:layout>
                </s:List>   
                <s:TextArea id="_chat"  width="100%" height="100%" minHeight="40" editable="false" fontSize="14" color="#000000" horizontalScrollPolicy="off"/>
            </mx:VDividedBox>
        </mx:HDividedBox>

    </s:VGroup>
</s:Group>

UPDATE:

Я должен добавить, что моя проблема (пользователи жалуются на воспроизводимые сбои Flash-плагина) началась после того, как я перешел с XML на JSON.

Я перезапустил Profiler и , почему он показывает 987 экземпляров JSON.parseCore , в то время как в лобби только 400 пользователей (и только немногие в настоящее время видимы в списках с виртуальными макетами)

enter image description here

Любые идеи, пожалуйста, что могло измениться, когда я перешел с XML на JSON? (Я ничего больше не трогал). Я действительно надеялся на лучшую производительность, потому что иногда читал об утечке памяти XML во Flex ...

1 Ответ

2 голосов
/ 16 марта 2012

Ваш код и функции выглядят очень просто. Я не думаю, что утечка памяти имеет место. Вы в состоянии воспроизвести "вялого" себя?

Я заметил, что у вас есть чат. Если текста будет много - приложение может работать медленно и зависать в какой-то момент. Например, ограничьте чат, чтобы отображать только последние 100 строк.

UPDATE: Только что посетил вашу игру и заметил, что пользователи имеют изображения высокого разрешения в качестве изображения профиля. Это может вызвать замедление при увеличении количества.

...