Окт
19

Garbage Collector и Android

Делаю вторую игру под Android — Cheepers'ов (несколько изменённую версию). Всё глубже погружаюсь в Android... Столкнулся с проблемой подвисания игры от нескольких миллисекунд до нескольких секунд. Вообщем, причина была ясна сразу — подвисания вызывает сборщик мусора. И не удивительно, ведь в процессе тестового уровня создаётся и уничтожается много спрайтов-чиперсов (более 1500).

Поискал в инете решения и нашёл. Как говорится, выбирай — либо нужна высокая скорость работы, либо меньшее использование памяти. В следующем посте я переведу доку «Designing for Performance», а пока продолжу.

Итак, как известно, сборщик мусора вызывается автоматически, когда требуется освободить память под новые нужды. Он удаляет все объекты, на которые уже никто не ссылается. Например, если спрайт добавлен на сцену — он находится в списке отображаемых объектов. Если на этот же спрайт мы ещё ссылаемся из собственного массива, то он уже имеет две ссылки.

В моей игре все отображаемые спрайты хранятся в массиве. При удалении спрайта со сцены я также его удаляю из массива (= null). Затем создаю новый спрайт, снова пихаю его в массив и добавляю на сцену. Со временем вступает в работусборщик мусора и вызывается подтормаживание, т.к. «мёртвых» спрайтов скапливается достаточно много.

Немного подумав, я решил не удалять «мёртвые» спрайты, а сохранять их в списке (типа кэша) и при создании следующего нового спрайта сначала просматривать список «мёртвых», а уж потом, если подходящего спрайта в нём не нашлось, создавать спрайт заново.

Таким образом, после «смерти» спрайт не исчезает в небытии, а удаляется со сцены и заносится в список (кэш). И сборщик мусора не запускается. И игра не тормозит.

При «восстании из мёртвых» спрайт удаляется из кэша и помещается на сцену.

Всё.

P.S. (Днём позже).

Игра всё еще тормозит. Но решение я нашёл.

Вот изменения:

1. Для спрайта создал поле isCached. Перед помещением его в кэш смотрю это поле — если isCached == true, то ничего не делаю. Иначе помещаю спрайт в кэш и присваиваю isCached = true.

2. После извлечении спрайта из кэша я его из кэша не удаляю.

3. Спрайт со сцены также не удаляю, а просто ставлю ему SetVisible (false) и setIgnoreUpdate (true)

Ещё нашёл небольшую ошибку, после которой число спрайтов в кэше уменьшилось с ~700 до 101.




5 комментариев к записи “Garbage Collector и Android”

  • Здравствуйте. Сделал для примера пэкмана. Один спрайт всего анимированный, но все равно подвисает из-за сборщика мусора. Создал я его в методе CreateResources, приаттачиваю в CreateScene (в andengine). Вы не знаете, в чем может быть проблема?

    • Так сложно сказать. Используйте профайлер — там хорошо видно, кто память жрёт

      • Не понял, о чем вы, разберусь, спасибо.

        А вообще, я вроде запустил на телефоне, там зависаний не видно, буду разбираться.

  • Здравствуйте.

    Делаю игру и возникла вот такая проблема:

    При запуске игры срабатывает сборщик мусора и падает фпс, а потом поднимается.

    Сделал тестовый проект в котором идёт только загрузка атласов и добавление спрайтов.

    Даже если загружаю всего 2 атласа(размером 1024 на 1024) всё равно срабатывает сборщик мусора.

    Если загружаю больше атласов(например 20 штук) то сборщик мусора срабатывает много раз и фпс падает до нуля, а потом восстанавливается до 60 фпс.

    Атласы загружаю вот так:

    Shining_Atlas[i] = new BitmapTextureAtlas (getTextureManager (),1024, 1024, TextureOptions.BILINEAR);

    Shining_Texture[i] = BitmapTextureAtlasTextureRegionFactory.createFromAsset (Shining_Atlas[i], this, «04.png», 0, 0);

    Shining_Atlas[i].load ();

    Вот несколько строк из лога:

    I/dalvikvm-heap (27491): Grow heap (frag case) to 3.058MB for 420016-byte allocation

    D/dalvikvm (27491): GC_FOR_ALLOC freed 1K, 12% free 2973K/3360K, paused 28ms, total 28ms

    D/dalvikvm (27491): GC_CONCURRENT freed <1K, 12% free 2973K/3360K, paused 4ms+3ms, total 34ms

    D/dalvikvm (27491): GC_FOR_ALLOC freed 1K, 12% free 2973K/3360K, paused 34ms, total 34ms

    I/dalvikvm-heap (27491): Grow heap (frag case) to 3.458MB for 420016-byte allocation

    D/dalvikvm (27491): GC_FOR_ALLOC freed <1K, 11% free 3383K/3772K, paused 19ms, total 19ms

    D/dalvikvm (27491): GC_CONCURRENT freed 410K, 22% free 2974K/3772K, paused 5ms+1ms, total 18ms

    D/dalvikvm (27491): GC_EXPLICIT freed 420K, 33% free 2555K/3772K, paused 2ms+2ms, total 24ms

    Я так понимаю что в heap недостаточно памяти и он запускает сборщик мусора чтобы освободить её.

    Запустил такие функции:

    Runtime.getRuntime ().totalMemory () 3010560

    Runtime.getRuntime ().freeMemory () 162536

    Runtime.getRuntime ().maxMemory () 268435456

    Если я правильно понимаю то для heap максимальный размер 268 мегабайт, но я посмотрел по DDMS там размер heap всего 3 мегабайта.

    Причём размер heap не зависит от количество загруженных атласов(при 20 загруженных атласах heap всё равно 3 мегабайта).

    По сути 2 атласа(1024 на 1024) это примерно 8 мегабайт, но почему-то не хватает памяти и он запускает сборщик мусора.

    Тестировал на реальном устройстве с 1гб оперативной памяти и Android 4.2

    Можете подсказать в чём может быть дело? Посмотрел в настройках запущенных приложений, там это приложение занимает не более 5 мегабайт. некоторые приложения ведь занимают и больше(например Subway Surfers занимает около 200 мегабайт)

    • К сожалению, не могу ничего сказать, давно не писал под Андроид. Может быть кто-то из читателей блока поможет. Напишите ещё в группу AndEngine в контакте

Прокомментировать