0

Hello I'm trying to add a tail behind player just like the snake game. But for some reason this keeps lagging out my game and running it out of memory. Why is this happening and how do I fix this?

I create the list like this:

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

This is where I create new objects and remove them in a 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);
        }
    }
}

This is how my snake class looks like:

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++;
    }
}
maja
  • 17,250
  • 17
  • 82
  • 125
Stefan
  • 1,905
  • 7
  • 24
  • 38
  • 1
    Is there any reason you are using `CopyOnWriteArrayList` over a regular `ArrayList`? Altering a `List` while iterating over it generally causes problems. Also there are some serious encapsulation issues with your code. – Dragondraikk Jan 30 '15 at 09:37
  • 1
    @Dragondraikk I know I'm gonna clean it later but I'm just wondering why snake.remove(currentSnake); Isn't removing it. If i try to log currentSnake in the next line it still finds it. Also I'm using the copyonwritearraylist so I can remove the currentSnake object otherwise this ends up with an error http://www.programcreek.com/2014/01/java-util-concurrentmodificationexception/ – Stefan Jan 30 '15 at 09:41
  • @Dragondraikk `CopyOnWriteArrayList` is `thread-safe` – mr.icetea Jan 30 '15 at 09:42
  • @mr.icetea True, but the GUI thread is usually single threaded. – Peter Lawrey Jan 30 '15 at 09:48
  • If you are getting out of memory error either a) you have a memory leak or b) you haven't given the process enough memory. I suggest you look at how the memory is being used to see if it's consumption is reasonable e.g. with `jmap -histo:live` or `jvisualvm` – Peter Lawrey Jan 30 '15 at 09:49
  • @PeterLawrey the problem is snake.remove(currentSnake); doesnt remove the currentSnake object. If I log currentSnake after that it still prints it out so snake.remove just doesnt work for some reason. – Stefan Jan 30 '15 at 09:53
  • @Stefan when you remove it from the list, is it removed from the screen? – Peter Lawrey Jan 30 '15 at 09:55
  • @PeterLawrey Yes Its dissapearing – Stefan Jan 30 '15 at 09:58
  • @Stefan so it is disappearing from the screen when you remove it from the list, so what is the problem? – Peter Lawrey Jan 30 '15 at 09:59
  • That I can still print it out when it should be removed? This makes it look like its not removing it. – Stefan Jan 30 '15 at 10:05
  • Don't use CopyOnWrite it will copy the whole internal array on each modification operations and should explain your memory issue. Use Iterators to loop and remove at the same time: Iterator it = snake.iterator(); while(it.hasNext()) {Snake s = it.next(); if(...) {s.Update();} else {{it.remove();}} – nomoa Jan 30 '15 at 10:07
  • @nomoa can you post this in a new answer? :-) – Stefan Jan 30 '15 at 10:08

4 Answers4

3

As stated by javadoc CopyOnWriteArrayList :

A thread-safe variant of ArrayList in which all mutative operations (add, set, and so on) are implemented by making a fresh copy of the underlying array.

So it's costly operation and this class is well designed for concurrent reads with a small modifications rate. It looks like it's not your case.

If you choosed this class to resolve an issue concerning reading a List while modifying it please consider using Iterators with ArrayList which are designed for that usage :

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();
    }
  }
}

Beware that ArrayList is not protected against concurrent access. If multiple threads are able to read and write to your list you have to protect it with synchronization blocks.

But reading your code I guess a design with a PriorityQueue seems to be more appropriate.

nomoa
  • 1,043
  • 6
  • 18
  • This for some reason ends up in crashing my game: Exception in thread "LWJGL Application" java.util.NoSuchElementException at java.util.concurrent.CopyOnWriteArrayList$COWIterator.next(Unknown Source) Do you have any idea why? – Stefan Jan 30 '15 at 10:28
  • You did not stop using the `CopyOnWriteArrayList`, did you? – Dave Jan 30 '15 at 10:32
  • you have to use ArrayList, but I suspect you have synchronization issues I will edit my answer – nomoa Jan 30 '15 at 10:33
  • @nomoa this still ends up crashing my code with the same exception error I stopped using the copyonwritearraylist and I'm using ArrayList instead. Also eclipse doesnt give any errors when compiling – Stefan Jan 30 '15 at 10:40
  • Wrap all access to snake ArrayList with synchronized(snake) { //code }. – nomoa Jan 30 '15 at 10:44
  • @nomoa Still the same error I just don't get whats wrong.... Exception in thread "LWJGL Application" java.util.NoSuchElementException at java.util.ArrayList$Itr.next(Unknown Source) – Stefan Jan 30 '15 at 10:47
  • Well I have no other clues :(, please double check (in all your code base) that all accesses to the list are actually protected with synchronization blocks. Please also check that Batch and ShapeRenderer does not access the list. – nomoa Jan 30 '15 at 10:54
  • @nomoa batch and shapenrenderer acces the list I think? because each time currentSnake.Update(); happens it draws with batch and shaperenderer – Stefan Jan 30 '15 at 10:57
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/69886/discussion-between-stefan-and-nomoa). – Stefan Jan 30 '15 at 10:59
1

I basically think you get java.util.ConcurrentModificationException exception because you are trying to remove elements (Snakes) while iterating over your List as @Dragondraikk mentioned.

This is not a good reason to use CopyOnWriteArrayList as you can read for example in enter link description here where it explains a bit the usefulness of CopyOnWriteArrayList. Basically CopyOnWriteArrayList is useful if

you mostly need to iterate the ArrayList and don't modify it too often

which you don't.

Why don't you just modify the List you have after having retrieved an element. If you are not satisfied with this behavior, i.e. you don't have a constant number of elements (I guess you don't) try to update them in a separate loop than removing/adding them.

Community
  • 1
  • 1
Eypros
  • 5,370
  • 6
  • 42
  • 75
1

You can't remove an item from a list while you are iterating through it. You need to remove snakes that need removing before updating the rest.

I would suggest:

snakes.removeIf(snake -> snake.createDate > snakeLength);
snakes.stream().forEach(Snake::Update);
sprinter
  • 27,148
  • 6
  • 47
  • 78
1

to answer the OPs question:

You are going OOM because you are using the CopyOnWriteArrayList which, and I quote the official docs is

A thread-safe variant of ArrayList in which all mutative operations (add, set, and so on) are implemented by making a fresh copy of the underlying array. (Emphasis added)

Dave
  • 1,784
  • 2
  • 23
  • 35