Java:为什么这个列表会让我的游戏“内存不足”

时间:2015-01-30 09:34:16

标签: java crash libgdx out-of-memory

您好我正试图像蛇游戏一样在玩家身后添加尾巴。但由于某种原因,这种情况不断延续我的游戏并将其从内存中运行。为什么会发生这种情况?如何解决这个问题?

我创建了这样的列表:

List<Snake> snake = new CopyOnWriteArrayList<Snake>();

这是我创建新对象并在forloop中删除它们的地方:

public void snake() {
    snake.add(new Snake(ball.getX(), ball.getY()));
    currentTime++;
    for(Snake currentSnake: snake) {
        if(currentSnake.creationDate < SnakeLength){
            currentSnake.Update();
        } else {
            Gdx.app.log("SnakeLength" + SnakeLength, "CreationDate" + currentSnake.creationDate);
            snake.remove(currentSnake);
        }
    }
}

这就是我的蛇类的样子:

public class Snake
{
    float width = Gdx.graphics.getWidth();
    float height = Gdx.graphics.getHeight();
    float screenwidth = width/270;
    float screenheight = height/480;

    public float x;
    public float y;
    public int creationDate;
    ShapeRenderer shapeRenderer;
    SpriteBatch batch;

    public boolean active = false;

    public Snake(float x, float y) {
        shapeRenderer = new ShapeRenderer();
        batch = new SpriteBatch();
        this.x = x;
        this.y = y;
    }

    public void Update() {
        batch.begin();
        shapeRenderer.begin(ShapeType.Filled);
        shapeRenderer.setColor(new Color(1, 1, 1, 0.2f));
        shapeRenderer.circle(x + 8*screenwidth, y + 8*screenheight, 6*screenheight);
        shapeRenderer.end();
        batch.end();
        creationDate++;
    }
}

4 个答案:

答案 0 :(得分:2)

如javadoc CopyOnWriteArrayList所述:

  

ArrayList的线程安全变体,其中所有可变操作(添加,设置等)都是通过制作基础数组的新副本来实现的。

因此,它的操作成本很高,而且这个类很好地设计用于具有较小修改率的并发读取。看起来不是你的情况。

如果您选择此类来解决有关在修改List时读取List的问题,请考虑使用针对该用法设计的具有ArrayList的Iterators:

synchronized(snake) { // Protect the snake list against concurrent access. Do it whenever you access the snake list
  for(Iterator<Snake> it = snake.iterator();; it.hasNext()) {
    Snake currentSnake = it.next();
    if(currentSnake.creationDate < SnakeLength) {
        currentSnake.Update();
    } else {
        Gdx.app.log("SnakeLength" + SnakeLength, "CreationDate" + currentSnake.creationDate);
        it.remove();
    }
  }
}

请注意,ArrayList不受并发访问保护。如果多个线程能够读取和写入列表,则必须使用同步块保护它。

但是阅读你的代码我觉得使用PriorityQueue的设计似乎更合适。

答案 1 :(得分:1)

我基本上认为你得到java.util.ConcurrentModificationException例外,因为你试图删除元素(Snakes),同时迭代你的List @Dragondraikk提到。

这不是使用CopyOnWriteArrayList的好理由,因为您可以在enter link description here中阅读,其中它解释了CopyOnWriteArrayList的有用性。基本上CopyOnWriteArrayList

时很有用
  

你最需要迭代ArrayList并且不要经常修改它

你不是。

为什么不修改检索到元素后的列表。如果您对此行为不满意,即您没有恒定数量的元素(我猜您不会)尝试在单独的循环中更新它们而不是删除/添加它们。

答案 2 :(得分:1)

在迭代时,您无法从列表中删除项目。在更新其余部分之前,您需要删除需要删除的蛇。

我建议:

snakes.removeIf(snake -> snake.createDate > snakeLength);
snakes.stream().forEach(Snake::Update);

答案 3 :(得分:1)

回答OP的问题:

你正在使用OOM,因为你使用的是CopyOnWriteArrayList,我引用official docs

  

ArrayList的线程安全变体,其中所有变异操作(添加,设置等)都由制作基础数组的新副本来实现。   (强调补充)