Управление памятью

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

Мы хотим иметь точный контроль над временем жизни наших ресурсов. В libGDX есть несколько классов, которые представляют такие ресурсы. Все они реализуют общий Disposable интерфейс, который указывает, что экземпляры класса должны быть удалены вручную в конце времени жизни.

Не освобождение ресурсов приведет к серьезным утечкам памяти.

Следующие классы должны быть удалены вручную.

  • AssetManager
  • Bitmap
  • BitmapFont
  • BitmapFontCache
  • CameraGroupStrategy
  • DecalBatch
  • ETC1Data
  • FrameBuffer
  • Mesh
  • ParticleEffect
  • Pixmap
  • PixmapPacker
  • ShaderProgram
  • Shape
  • Skin
  • SpriteBatch
  • SpriteCache
  • Stage
  • Texture
  • TextureAtlas
  • TileAtlas
  • TileMapRenderer
  • World

Ресурсы должны быть удалены, как только они больше не нужны, освобождая связанную с ними память.

Доступ к удаленному ресурсу

Доступ к ресурсу, который уже удален, приведет к неопределенным ошибкам, поэтому убедитесь, что все ссылки, указывающие на удаленные ресурсы, очищены.

При сомнениях в том, нужно ли удалять ресурсы определенного класса или нет, проверьте его на наличие disposed() метода. Если он есть, значит, вы работаете с нативным ресурсом и потом должны его удалить в libGDX игре или приложении.

Объединение объектов

Объединение объектов является принципом повторного использования неактивных или "мертвых" объектов, вместо постоянного создания новых. Это достигается с помощью создания пула объектов, и когда вам нужен новых объект, вы получаете его из этого пула. Если в пуле есть свободный объект, то он его вернет. Если пул пустой или не содержит свободных объектов, то возвращается созданных новый экземпляр объекта. Когда объект больше не нужен, вы освобождаете его и, это означает его возвращение обратно в пул. Таким образом, повторно используется память для размещения объектов и сборщик мусора просто счастлив.

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

libGDX предоставляет интерфейсы и классы для легкой работы с пулом.

Реализация Poolable интерфейса означает, что объект имеет reset() метод, который в libGDX автоматически вызывается при освобождении объекта.

Ниже приведен краткий пример пула для пуль.

public class Bullet implements Poolable {

    public Vector2 position;
    public boolean alive;

    /**
     * Конструктор пули. Простая инициализация переменных.
     */
    public Bullet() {
        this.position = new Vector2();
        this.alive = false;
    }

    /**
     * Инициализация пули. Вызовите этот метод после получения пули из пула.
     */
    public void init(float posX, float posY) {
        position.set(posX,  posY);
        alive = true;
    }

    /**
     * Метод обратного вызова, когда объект освобожден.
     * Автоматически вызывается Pool.free() методом.
     * Необходимо сбросить каждое смысловое поле данной пули
     */
    public void reset() {
        position.set(0,0);
        alive = false;
    }

    /**
     * Метод вызывается при каждом кадре и обновляет пулю.
     */
    public void update (float delta) {

        // обновление позиции пули
        position.add(1*delta*60, 1*delta*60);

        // если пуля вне экрана, установка состояние в "мертвая"
        if (isOutOfScreen()) alive = false;
    }
}

В классе игрового мира:

public class World() {

    // Массив, содержащий активные пули.
    private final Array<Bullet> activeBullets = new Array<Bullet>();

    // Пул для пуль.
    private final Pool<Bullet> bulletPool = new Pool<Bullet>() {
        @Override
        protected Bullet newObject() {
            return new Bullet();
        }
    };

    public void update(float delta) {

        // если вам нужно создать новую пулю:
        Bullet item = bulletPool.obtain();
        item.init(2, 2);
        activeBullets.add(item);

        // если вы хотите освободить "мертвые" пули, верните их обратно в пул:
        Bullet item;
        int len = activeBullets.size;
        for (int i = len; --i >= 0;) {
            item = activeBullets.get(i);
            if (item.alive == false) {
                activeBullets.removeIndex(i);
                bulletPool.free(item);
            }
        }
    }
}

Класс Pools предоставляет статические методы для динамического создания пулов любых объектов (используя ReflectionPool). В приведенном выше примере, это можно использовать в libGDX игре так:

private final Pool<Bullet> bulletPool = Pools.get(Bullet.class);