| « top4top - перемены | top4top - процесс разработки » |
Профилирование ActionScript 3.0 проекта
Если Вы не раз профилировали проект, то что-то интересное, возможно, найдете ближе к концу поста.
Вступление
На данный момент единственный инструмент для профилирования это Flex Builder Profiler (хотя необходимый профайлеру API открыт - делай кто хочешь).
В процессе разработки top4top.ru не раз возникали проблемы с утечками памяти, иногда для их устранения приходилось даже вызывать разработчиков в офис по ночам. Иногда память не освобождалась вообще - росла с шагом 50-200 mb от числа переходов по страницам:

Это очень заметно и чинятся, как правило, за несколько часов. Сложнее сказать, есть ли утечка памяти, и где она по такому графику:

Тут уже мало что понятно. Поэтому первый вопрос - как определить, есть ли в приложении утечка памяти.
Определение факта утечки памяти - общие моменты
Тут нам очень поможет функция взятия разности между двумя dump-ами памяти приложения:

Результат - таблица Loitering Objects View, цитата из хелпа:
The Loitering Objects view shows you the differences between two memory snapshots of the application that you are profiling. The differences that this view shows are the number of instances of objects in memory and the amount of memory that those objects use. This is useful in identifying memory leaks. The time between two memory snapshots is known as the snapshot interval.
Для нас в ней самое интересное это имена классов и количество их оставшихся экземпляров.

На схеме выше приведен первый приходящий в голову вариант по поиску memory-leak-ов на Главной Странице. Однако он не работает - в список “бездомных” объектов попадают не только лики с Главной страницы, но и все объекты, создаваемые в Профайле 1.
Определение факта утечки памяти - стратегии
Можно придумать много разных стратегий. На картинке ниже прямоугольниками обозначены состояния системы, в которых инстанциируются объекты. При уходе из состояния его объекты должны удаляться. У нас в top4top состояния были страницами сайта, иногда - разделами (главная, чат).

Устранение утечки
- В простых случаях достаточно просмотреть код остающихся в памяти классов для устранения ошибки
Если она не очевидна, то тут нам поможет окошко Object References, которое показывает все ссылки на объект и открывается по дабл-клику на элементе в таблице Loitering Objects:

Сложнее всего искать утечки, из-за которых не удаляется вообще ничего. В этом случае два предыдущих способа не работают.
Именно такая ситуация у нас и была. Утечка находилась в часто исползуемом визуальном компоненте (назывался HtmlText). При создании компонента-страницы LayoutIndex в него где-то глубоко добавлялся экземпляр HtmlText и с тех пор вся иерархия (даже после удаления компонента всей страницы из дисплей-листа) становилась неуязвима для Garbage Collector’а.

Способ борьбы тут оказался довольно простой:
- package
- {
- import flash.display.Sprite;
- import flash.events.Event;
- public class test extends Sprite
- {
- private var picture:Picture;
- private var mainText:HtmlText;
- private var enterButton:SomeButton;
- private var closeButton:AnotherButton;
- public function test()
- {
- initContent();
- }
- private function initContent():void
- {
- picture = new Picture();
- addChild(picture)
- // Дальше в том же духе инициализируем и
- // добавляем другие компоненты
- // И подписываемся на удаление со сцены
- addEventListener(Event.REMOVED_FROM_STAGE, onRemovedFromStage, false, 0, true);
- }
- // Удаляем все ссылки на дочерние элементы
- private function onRemovedFromStage(event:Event):void
- {
- picture = null;
- mainText = null;
- enterButton = null;
- closeButton = null;
- while (numChildren > 0)
- removeChildAt(0);
- }
- }
- }
Таким образом в памяти остаются только “текущие” объекты и мы с легкостью с помощью таблицы Loitering Objects их удаляем.
Делать подобные удаления всегда или добавлять этот код только в процессе устранения ликов - это отдельное решение.
Также у нас действует стандарт, что все подписки на события должны быть объявлены с Weak Reference, если нет четких противопоказаний. За весь проект случаев, когда нужна была подписка с жесткой ссылкой было всего несколько. false, 0, true!
Напоследок - регулярное выражение для поиска не-weak подписок: addEventListener\(([\w.\"]*), (\w*)\) (автор: Алексей «Vooparker» Аникутин).
Трекбек адрес этой записи
URL трекбека (щелкните правой кнопкой мыши и скопируйте ссылку)
10 комментариев
Спасибо. Хорошая статья.
странно. не так давно в руФлеш Дембицкий отстаивал точку зрения, что викреференс не стоит использовать. насколько я помню, он тоже участвовал в этом проекте
Либо это было давно, либо где-то ошибка. У всей команды и у него тоже было единое мнение на этот счет - ставить везде, где можно.
http://groups.google.com/group/ruFlash/browse_frm/thread/4cb9b9fd4a34250/7ad9d85ade8283fa?lnk=gstп.с. antispam test сложный
whitered: веский аргумент
2-5 рисунок не отображаются, можете пофиксить? А статейка хорошая, надо сохранить.
Хорошая статья, и, по-моему, первая по профайлеру на русском вообще.Сэнкс.
Респект, Макс! Отличная статья получилась!
Спасибо за статью, думаю многим она поможет в критические дни.Раз уж затронули WeakReference и мое к нему отношение, выскажусь.
WeakReference - прекрасная вещь, если у вас грязный и неуклюжий код, который вы не понимаете и понимать не хотите. В этом случае вы на всякий случай втыкаете WeakReference и спасаетесь от фатальной утечки памяти. И это работает. Хоть и не всегда, но работает.
Отличная статья, спасибо.




