8、内存管理(Memory management)

游戏是资源繁多的应用程序,图像和声音效果会占用大量 RAM 。 此外,这些资源大多数不由Java垃圾收集器管理,而是由本机的驱动程序管理。让垃圾收集器决定何时从视频 RAM 中释放 5M 字节的纹理也不是一个太明智的想法。我们希望对资源的生命周期进行细致的控制。libgdx中有多个类代表这些资源。它们都实现了一个公共的 Disposable 接口,该接口指示此类的实例需要在生命周期结束时手动处理,如果未能处置资源将导致严重的内存泄漏。

下列的类需要手动的释放资源(可能不完整):

  • AssetManager
  • Bitmap
  • BitmapFont
  • BitmapFontCache
  • CameraGroupStrategy
  • DecalBatch
  • ETC1Data
  • FrameBuffer
  • Mesh
  • Model
  • ModelBatch
  • ParticleEffect
  • Pixmap
  • PixmapPacker
  • Shader
  • ShaderProgram
  • Shape
  • Skin
  • SpriteBatch
  • SpriteCache
  • Stage
  • Texture
  • TextureAtlas
  • TileAtlas
  • TileMapRenderer
  • com.badlogic.gdx.physics.box2d.World
  • all bullet classes

资源应该在它们不再被需要的时候尽可能快的处理掉,释放与它们相关的内存。访问一个已经被处置(dispose)的资源将会导致未定义的错误,所以要确保你已经清除了所有已经释放了内存的引用。

当对于一个特定的类是否需要 disposed 持有疑问,检查一下这个类是否有 dispose() 方法并且实现了 Disposable 接口。如果情况是这样,那么你现在就在和一个本地资源打交道。

一个可行的列出所有需要手动释放内存类的方法是:将libgdx克隆到您的系统,然后使用文本编辑器(如Sublime Text或其他)打开其libgdx根目录,搜索 implements Disposable, 它将显示所有源文件中需要在完成后调用 dispose() 的结果, 请注意,从此类结果类扩展的所有其他类也适用,这样就能获取到所有类的列表。 请注意,结果中的一些类来自libgdx本身的测试类,这与您的兴趣无关。

对象池(Object pooling) 

对象池是一种重用未活动或者“死”对象的机制,而不是每次都创建一个新的对象。这是通过创建一个对象池实现的,当你需要一个新的对象时,你可以从这个池中获取一个新对象。如果对象池内存在可用的(空闲)对象,这个对象将被返回。当你不再需要一个对象时,你释放这个对象,这意味着对象将被返回对象池。通过这种方式,对象申请的内存得以复用,垃圾回收器将会减轻很多工作。

对于将会频繁使用的对象使用对象池十分重要,例如子弹、障碍物、怪兽等等。

Libgdx 提供了一组方便使用对象池的工具。

实现 Poolable 接口意味着你的对象将会拥有 reset() 方法, 当你释放对象的时候将会被自动调用。

下面是一个子弹对象使用对象池的小例子。

public class Bullet implements Poolable {

    public Vector2 position;
    public boolean alive;

    /**
     * Bullet 构造器. 仅仅用来初始化变量
     */
    public Bullet() {
        this.position = new Vector2();
        this.alive = false;
    }
    
    /**
     * Initialize the bullet. Call this method after getting a bullet from the pool.
     * 初始化子弹对象,当从对象池获取一个子弹对象后调用这个方法。
     */
    public void init(float posX, float posY) {
    	position.set(posX,  posY);
    	alive = true;
    }

    /**
     * Callback method when the object is freed. It is automatically called by 
        Pool.free()
     * 对象被释放后的回调方法, 这个方法被 Pool.free() 方法自动调用
     * Must reset every meaningful field of this bullet.
     */
    @Override
    public void reset() {
        position.set(0,0);
        alive = false;
    }

    /**
     * Method called each frame, which updates the bullet.
     * 每帧调用的方法, 用来更新子弹对象
     */
    public void update (float delta) {
    	
    	// update bullet position
    	position.add(1*delta*60, 1*delta*60);
    	
    	// if bullet is out of screen, set it to dead
    	if (isOutOfScreen()) alive = false;
    }
}

在你的 World 类中

public class World {

    // array containing the active bullets.
    // 数组包含存活的子弹对象
    private final Array<Bullet> activeBullets = new Array<Bullet>();

    // bullet pool.
    // bullet 对象池
    private final Pool<Bullet> bulletPool = new Pool<Bullet>() {
	@Override
	protected Bullet newObject() {
		return new Bullet();
	}
    };

    public void update(float delta) {
	
        // if you want to spawn a new bullet:
        Bullet item = bulletPool.obtain();
        item.init(2, 2);
        activeBullets.add(item);

        // if you want to free dead bullets, returning them to the pool:
    	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 和黑科技)。在上面的例子中就是这么使用的。

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

如何使用对象池

一个 Pool<> 管理了一个对象的简单类型, 所以它需要事先按照该类型进行参数化。通过调用 Pool 对象的 obtain() 方法来获取对象,调用 free() 方法来回收对象。对象池中的对象也可以选择实现 Pool.Poolable 接口(只需要实现 reset() 方法),在这种情况下,对象池将会自动重置返回对象池的对象,对象是按需分配的(所以当你从没调用过 obtain() 时, 对象池将不会包含任何对象)。

你必须实现你自己 Pool<> 的子类, 因为 newObject() 方法时抽象的。

对象池注意事项

谨防丢失在已经被池化对象的引用。因为你调用 free() 方法不会废止任何任何存留的引用。 日过你不小心的话,这将导致很多微妙的bug。同时,当你的对象放入对象池却没有完全被重置时,也会导致未知的 Bug 。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值