Почему Sytem.totalMemory продолжает расти? - PullRequest
7 голосов
/ 20 июня 2009

У меня есть этот пустой проект, просто чтобы проверить переменную System.totalMemory. Насколько я вижу, я получаю следующие значения:

3076
3092
3096
3088
3092
3096
3100
3104
3108
3112
3117
3121
3125
3129
3133
3137
3141
3145
3149
...
And so on

У меня не было ни открытой Flash, ни интернет-браузера, ни другого экземпляра Flash.

Проект пустой, только один статический текст и один динамический текст, называемый «память». Один файл *. As , содержащий этот код:

package{
    import flash.display.Sprite;
    import flash.events.Event;
    import flash.system.System;
    import flash.text.TextField;
    public class Test extends Sprite {
        public function Test() {
            this.addEventListener(Event.ENTER_FRAME,Loop);
        }
        public function Loop(e:Event) {
            memory.text = String(System.totalMemory);
        }
    }
}

Это мои настройки публикации.

http://i.stack.imgur.com/3k1vq.png http://i.stack.imgur.com/rwM1D.png

Я тестировал в Отладке и публикации * .swf, те же результаты.

Я понятия не имею, поэтому, пожалуйста, помогите.

Ответы [ 5 ]

25 голосов
/ 21 июня 2009

Я думаю, вы ошиблись.

Во-первых, в ваших трассах отображается TotalMemory, усекающая последние 3 цифры (поскольку вы не делаете этого в коде, я предполагаю, что это из-за ширины TextField). Он растет примерно так: 3076, 3092, 3096 и т. Д. Это (примерно) килобайты, а не байты. Затем вы комментируете: «Всего памяти через 2 часа: 3887104. Боже мой». Теперь, если под 3,887,104 вы подразумеваете 3,887,104 Кб, это будет около 3,8 Гб. Я сомневаюсь, что это так, поэтому давайте предположим, что вы имеете в виду 3887 104 байтов. Это около 3800 Кб или 3,8 Мб. Не так много памяти, на самом деле, и, что более важно, не так далеко от ваших начальных 3076 Кб.

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

Во-вторых, хотя код очень прост, он потребляет память. Для начала, каждый раз, когда отправляется событие ENTER_FRAME, создается объект Event, который, в свою очередь, содержит ссылки на другие объекты, строки и т. Д. Это требует памяти. Затем вы неявно преобразуете число в строку (печатая totalMemory). Это также занимает память, независимо от того, выполняете ли вы явное преобразование или нет (то же самое применимо, если вы делаете трассировку вместо использования текстового поля). Вдобавок ко всему, наверняка происходят другие вещи, которые не очевидны с «точки зрения ActionScript».

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

Это становится более очевидным, если вы измените код для вычисления нескольких вещей.

package{
    import flash.display.Sprite;
    import flash.events.Event;
    import flash.system.System;
    import flash.text.TextField;
    import flash.utils.getTimer;
    import flash.text.TextField;
    import flash.text.TextFormat;

    public class Test extends Sprite {

        private var peak:int            = 0;
        private var prev:int            = 0;
        private var cur:int             = 0;
        private var diff:int            = 0;
        private var decreaseCount:int   = 0;
        private var increaseCount:int   = 0;
        private var accumIncrease:int   = 0;
        private var accumDecrease:int   = 0;
        private var maxIncrease:int     = 0;
        private var maxDecrease:int     = 0;
        private var initTime:Number     = 0;
        private var elapsed:Number      = 0;

        private var time:TextField;
        private var info:TextField;

        public function Test() {
            initTime = getTimer();
            var tf:TextFormat = new TextFormat("Courier New",12);
            time = new TextField();
            time.defaultTextFormat = tf;
            time.width  = 250;
            addChild(time);
            info = new TextField();
            info.defaultTextFormat = tf;
            info.y = 15;
            info.width  = 250;
            info.height = 250;
            addChild(info);
            addEventListener(Event.ENTER_FRAME,Loop);
        }

        public function Loop(e:Event) {
            cur = System.totalMemory >> 12;

            elapsed     = (getTimer() - initTime) / 1000; 
            time.text   = "time running:        " + elapsed;

            if(cur == prev) {
                return;
            }

            if(cur > peak) {
                peak = cur;
            }

            if(cur > prev && prev > 0) {
                diff = cur - prev;
                if(diff > maxIncrease) {
                    maxIncrease = diff;
                }
                accumIncrease += diff;
                increaseCount++;
            } else if(cur < prev) {
                diff = prev - cur;
                if(diff > maxDecrease) {
                    maxDecrease = diff;
                }
                accumDecrease += diff;
                diff = -diff;
                decreaseCount++;
            }

            info.text   =   "current:           " + cur + "\n"
                        +   "previous:          " + prev + "\n"
                        +   "diff:              " + diff + "\n"
                        +   "peak:              " + peak + "\n"
                        +   "increaseCount:     " + increaseCount + "\n"
                        +   "decreaseCount:     " + decreaseCount + "\n"
                        +   "accumIncrease:     " + accumIncrease + "\n"
                        +   "accumDecrease:     " + accumDecrease + "\n"
                        +   "maxIncrease:       " + maxIncrease + "\n"
                        +   "maxDecrease:       " + maxDecrease;

            prev    = cur;

        }
    }
}

Я использую блоки размером 4096 байт в качестве единицы (вот почему я делаю System.totalMemory >> 12. Просто причудливый способ сказать System.totalMemory / 4096). Я думаю, что это более управляемо, и в любом случае totalMemory всегда возвращает кратные 4096 байтов или 4 КБ. Вы можете прочитать больше о GC Flash здесь: https://developer.mozilla.org/en/MMgc. Эта часть плеера с открытым исходным кодом, и вы даже можете прочитать источники, если вы так склонны.

Краткое объяснение того, что код отслеживает:

  • время выполнения: Прошло несколько секунд с момента запуска SWF
  • текущий: Объем памяти, возвращаемый System.totalMemory, кусками по 4 Кбайт
  • предыдущее: предыдущее значение totalMemory
  • diff: Разница между текущим и предыдущим. Может быть отрицательным. Это показывает, увеличилось или уменьшилось использование памяти относительно предыдущего значения.
  • пик: Самоочевидно. Это не очень важно.
  • увеличенияCount: Количество раз, когда ток был больше, чем предыдущий. По сути, он сообщает, во сколько раз был увеличен общий объем памяти, по крайней мере, на 1 кусок.
  • lowerCount: Количество раз, когда предыдущий был больше текущего. Это скажет вам, сколько раз была освобождена память.
  • аккумуляторIncrease: Накопленное значение положительных различий. Даст вам знать, сколько кусков было выделено.
  • АКБДирект: Накопленное значение отрицательных различий. Даст вам знать, сколько кусков было выпущено.
  • maxIncrease: Максимальное количество чанков, выделенных в двух выполнениях цикла.
  • maxDecrease: Максимальное количество чанков, выпущенных за два выполнения цикла.

Теперь давайте посмотрим на некоторые «снимки», сделанные с использованием этого кода.

Это ранний снимок, сделанный, когда SWF работал в течение 3 секунд. Просто обратите внимание, что текущий читает 760.

  • время работы: 3 с
  • ток: 760
  • предыдущий: 759
  • diff: 1
  • пик: 760
  • увеличениеCount: 3
  • уменьшениеCount: 0
  • увеличение на 6: 1085 *
  • нужное уменьшение: 0
  • maxIncrease: 3
  • maxDecrease: 0

Примерно через 10 минут:

  • время работы: 574 с
  • ток: 763
  • предыдущий: 762
  • curDiff: 1
  • пик: 834
  • увеличениеCount: 127
  • уменьшениеCount: 3
  • увеличение: 132
  • общее снижение: 123
  • maxIncrease: 3
  • maxDecrease: 72

Несколько замечаний:

  1. Примерно через 10 минут ток очень близко к тому, что было в 3 сек: 763 против 760. Это значит прямо сейчас, общая память составляет 3,052 Мб; В 3 с, это было 3040 Мб.
  2. Количество увеличений велико, и Количество уменьшений мало. Это означает игрок выделил память много раз, но выпустили очень экономно.
  3. maxIncrease низкое и maxDecrease в приоритете. Добавьте это к 2), и у вас есть интересная картина: игрок выделяет небольшое количество кусков часто. Это выпускает их в гораздо более медленный темп; когда это делает, однако, это выпускает большое количество фрагментов.
  4. Накопления и увеличения накопления тоже очень близко.

Теперь позвольте SWF запустить еще немного времени. После 50 минут работы снимок выглядит так:

  • время: 2989 с
  • ток: 931
  • предыдущий: 930
  • diff: 1
  • пик: 931
  • увеличениеCount: 690
  • уменьшениеCount: 8
  • увеличение: 699
  • общее снижение: 522
  • maxIncrease: 3
  • maxDecrease: 163

В этот момент вы можете подумать, что есть утечка. Обратите внимание, что текущая память 931, по сравнению с начальной 760.

Но посмотрите, что происходит за 3124 секунды, ~ 52 минуты:

  • время работы: 3142 с
  • ток: 767
  • предыдущий: 768
  • diff: -1
  • пик: 962
  • увеличениеCount: 720
  • уменьшениеCount: 10
  • увеличение: 730
  • общее снижение: 717
  • maxIncrease: 3
  • maxDecrease: 194

До того, как GC вступил в силу, пик вырос до 962. Но после этого ток снизился до 767, опять же, очень близко к начальным 760.

Итак, если говорить об этом, то факт увеличения использования памяти не обязательно означает, что произошла утечка. Вам просто нужно иметь дело с тем фактом, что игрок собирает мусор, и этот процесс не является детерминированным. Память в конечном итоге будет восстановлена ​​в какой-то момент (если, конечно, у вас нет утечки в вашем коде). Вы не можете определить, когда это произойдет. Это произойдет, когда игрок определит, что это необходимо. И вообще, игрок знает лучше.

Тем не менее, я думаю, что важно обратить внимание на возможные утечки в вашем коде. Но просто отслеживание System.totalMemory не поможет вам определить это. Если вы можете, используйте инструмент, такой как профилировщик памяти Flex Builder, который не идеален, но дает вам гораздо больше полезной информации. И будьте осторожны при добавлении слушателей на сцену и при использовании таймеров, самые большие причины утечки памяти во флеш-плеере.

1 голос
/ 20 июня 2009

Вы можете проверить, какая версия проигрывателя Flash работает на вашем компьютере. Если у вас установлена ​​IDE, есть неплохая вероятность того, что вы работаете в отладочной версии плеера, которая обычно не освобождает память почти так же часто (если вообще) по сравнению с обычным плеером.

Если вы работаете в отладочном плеере, вы можете принудительно запустить gc sweep - System.gc ();

0 голосов
/ 22 июня 2009

Возможно, это очевидно, но помните, что System.totalMemory предоставляет объем памяти, используемый Flash Player (статический). Если у вас открыто другое приложение Flash, это может объяснить, почему увеличивается объем вашей памяти.

0 голосов
/ 20 июня 2009

Насколько я могу судить, в каждом цикле вызов System.totalMemory возвращает значение uint, представляющее объем памяти в байтах, используемый в текущий момент Flash. Uint, возвращаемый вызовом System.totalMemory, сохраняется в памяти (т. Е. В вашей оперативной памяти, а не в текстовом поле, которое вы пометили как «память»). Ссылка на memory.text затем обновляется, чтобы указывать на пространство в памяти, занимаемое uint. Старый уинт остается в памяти, но на него нет никаких ссылок. Uints - это 32-битные (4 байта) данные, поэтому вы видите, что объем памяти увеличивается каждый раз на 4 байта. Как только сборщик мусора запустится, пространство в памяти, занимаемое теперь разыменованными uints, должно освободиться, и вы увидите падение памяти.

Однако, когда я запускаю ваш пример кода, я не получаю ничего подобного. Он идет вверх и вниз, он не идет последовательно вверх, как у вас, но я на OS X, так что это другой плеер.

0 голосов
/ 20 июня 2009

При изменении текста в текстовом поле Flash будет загружать различные символы в память для их отображения. Это может объяснить несколько сотен байтов увеличения, которое вы получаете.

Если оставить в покое, насколько больше занимает площадь?

ОБНОВЛЕНИЕ: Подумав об этом, я думаю, что это связано с временной растровой графикой, которая создается для Flash для рендеринга текста ... хотя вы не ожидаете этого с помощью трассировки ... хотя трассировка должна делать внутренние вещи тоже ...

Думали ли вы запустить его во FlexBuilder? У него есть инструмент профилирования, который может сказать вам, куда движется ваша память.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...