1

I am creating a game for android. And i have noticed that whenever the garbage collector kicks in, the game experiences a large amount of lag.

When the App is first run, all of the objects are created and added into arrayLists as follows:

br1 = ImgLoader.getResizedBitmap(BitmapFactory.decodeResource(
            context.getResources(), R.drawable.branchfl, MainActivity.opt),
            MainActivity.height / 12, MainActivity.width / 6);

for (int i = 0; i < 10; ++i) {
        branches_available.add(new Sprite("Branch", br1, 0, 0));
    }

The Sprite object consists of a Bitmap, and several methods that allow me to modify said Bitmap. So in the above code, im storing 10 Sprites into an ArrayList. Now later in the code, i need to generate said sprites into my level dynamically. So all i do is this:

if (rand == 1) {
            branches_available.get(0).image = bToAdd;
            branches_available.get(0).xPos = (screenWidth - treeWid - bToAdd
                    .getWidth()) + bToAdd.getWidth() / 18;
            branches_available.get(0).yPos = 0 - bToAdd.getHeight();
            branches_available.get(0).height = bToAdd.getHeight();
            branches_available.get(0).width = bToAdd.getWidth();
            branches.add(branches_available.get(0));
            branches_available.remove(0);

This takes the object from the first array and adds it to the second array. And in the draw method, only the second arrayList is drawn. Now when the objects leave the screen, i do the following:

branchesSize = branches.size();
    for (int i = 0; i < branchesSize; ++i) {
        branches.get(i).move(0, speed);

        if (branches.get(i).yPos > screenHeight) {
            branches_available.add(branches.get(i));
            branches.remove(i);
            i--;
            branchesSize -= 1;
            // System.gc(); // Force Garbage Collection
        }
    }

So they are added back into the available array so they can be reused again in the above method if need be.

Despite doing this, i am still getting a large amount of lag in my application. Could it be because im removing the object from the array?

What am i doing wrong?

Thank you!

  • I believe this is why certain game developers write natively (if on Android, through JNI), so they can control garbage collection manually. There are certain designs that could _help_ with this, but not completely mitigate the effects. – Justin Jasmann Mar 03 '14 at 00:53
  • Here's two helpful links: http://stackoverflow.com/questions/2484079/how-can-i-avoid-garbage-collection-delays-in-java-games-best-practices and https://code.google.com/p/libgdx-users/wiki/ForceGarbageCollection. – Justin Jasmann Mar 03 '14 at 00:56
  • 1
    Thank you for the links. But ive already extensively researched both sources and applied them to my code. TO no avail –  Mar 03 '14 at 01:04
  • In c++ you can create a memory manager of sorts that will help you pre allocate all the memory you might need among other advantages. In java the exact same thing is possible, but you have to wonder. Could you possibly create a byte array or byte buffer and keep your images/sounds here? then when you are done with a certain byte array write over it. In this way your memory will never exceed the limit you set. This link might help one along this path. http://www.kdgregory.com/?page=java.byteBuffer – WIllJBD Mar 03 '14 at 01:29
  • That sounds like a viable option. Ill look into it :). One just has to wonder how games like flappy bird work. I looked at the source code. Nothing special like that in there –  Mar 03 '14 at 01:31
  • 1
    In my games, (I have 2 on the market, one using canvas, one using OpenGL) I have a sound manager, and for the canvas built game I have a bitmap/sprite manager. This calls Bitmap.recycle & system..gc to notify java to collect the garbage asap. on bitmaps I expect to be done with for a amount of time, and keeps bitmaps I need most in memory. If the bitmap has been recycled, next i need it, I reload it. Other things to do are optimize your sprite sheets to be as small as possible (memory wise). Also above I meant to say that in Java the exact same thing as in c++ is not possible. – WIllJBD Mar 03 '14 at 01:33
  • So what i have done above. With using the two arrayLists, is not efficient, since they are still in memory? Also, whenever i call System.gc() the program stutters as well. Even though i have not created any objects during the running of the game. –  Mar 03 '14 at 01:56
  • Do you see any garbage collection logs? When you are using an `ArrayList`, removing an element from the front or middle, will cause all the elements to be shifted one to the left to fill the blank space. –  Mar 03 '14 at 08:14
  • 1
    Mirroring user2046264's comment: Are you sure this is GC? How large is the list? Removing elements from `ArrayList` is `O(n)`, and since you're doing it in a loop over `n`, you get `O(n^2)` (slow) performance with an `ArrayList`. You could consider using a `LinkedList` with the `iterator.remove()` feature to get `O(1)` removal operations: http://stackoverflow.com/questions/322715/when-to-use-linkedlist-over-arraylist – user2221343 Jul 28 '15 at 13:27

1 Answers1

1

Try using a HashMap<String,Bitmap> and initialize it such that there is a key for every image you need, and make the key the image name. Then create a function, where you can ask for the image using the key, and the function will look for it in the map. If the bitmap at the key is null, then load the bitmap and set it to the key. When needed you can iterate over the hashmap using an iterator. Every time you load a bitmap, track the size of all bitmaps in the map by adding the size of the new bitmap to a member field. When the total size of all bitmaps starts to near a threshold you set, figure out a way of choosing bitmaps that are no longer needed at this second, and call bitmap.recycle, set the value at that key to null, subtract the size of the removed bitmap from the total size of all bitmaps, and notify the garbage collector.

This will solve some performance issues with array lists, and help to clean memory as fast as possible. The tricky thing will be deciding what bitmaps to release, and if you want, predicting or knowing before needed: which bitmaps to get into memory.

When drawing sprites, the easiest way is to know the offset, and the current sprite. For example if you have a 500x200 sprite, that has 5 frames on the first row, and 5 frames on the second row (so 5 columns 2 rows), and you know that the progression of the sprites it left to right, so from (0,0) to (5,0) and then down 1 and left to right again (1,0) to (1,5) and then back to start at (0,0), then you can create a simple function that will give you what you need.

In both OpenGL and Canvas, I was able to use the offsets to calculate which portion of the whole bitmap to draw depending on the current frame, so that I never had more than 1 copy of the bitmap in memory at any time, even if there were 100 on screen objects using it.

Finally as I said before in the comments, you will want to minimize as much as possible the size of all the sprite sheets.

WIllJBD
  • 6,144
  • 3
  • 34
  • 44