Using Libgdx features to avoid garbage collection

On one hand, unmanaged languages leave the responsibility of allocating and deallocating memory to the programmer, which allows them to choose the most convenient moment to make use of the required system resources and thus avoiding stuttering.

On the other hand, Java has a double-edged feature that handles the process of deallocating memory automatically whenever an object is no longer in use. This is commonly known as garbage collection. Every now and then, the GC kicks in and checks whether there are unreferenced objects to free up, but this takes time (and can occur in a nonsuitable moment within the game execution).

Sometimes Java programmers rely on its effectiveness and tend to forget about memory stuff when developing for desktop. However, it will not take a lot to realize about the need to manually deal with it when publishing for mobile platforms as their games experience slowdowns from time to time.

Once more, it is important to highlight the critical nature of videogames as a particular type of simulation that directly interacts with the user. This means that one of our highest priorities must be to give the best and smoothest user experience possible. Consequently, we need to have under control the CPU workload at every stage and therefore the garbage collector.

Getting ready

This recipe contains a set of tips along with small code snippets, so a separate code file will not be necessary.

How to do it…

Libgdx is aware of the potential garbage collector malice; hence, it includes some features to minimize its impact. In the next lines, you will also find some general tips that apply to any Java game.

Before diving into particular considerations, the golden rule to avoid garbage collection is not to allocate temporary memory, putting special emphasis within the game loop considering that mistakes are maximized under this context. Instead, cache the information as member variables so as the garbage collector will perform its laborious work very rarely.

Collections

In addition to putting a great deal of effort into having multiplatform support with high performance, Libgdx collections concern about creating garbage. Proof of this is that they are more memory efficient than java.utils.collections.

Apart from that, the Array class always returns the same instance when calling its iterator() method, which implies that it must not be used in nested loops. This pursues the goal of keeping GC from running frequently.

Note

The ArrayIterator constructor allows you to instance new iterators for the Array class.

Caching

Following the maxim of not creating new objects, paying special attention to critical sections such as loops, it is interesting to consider using one of the most powerful optimization techniques: caching. A clear example is a typical for loop over an ArrayList just like the following code:

ArrayList<Foo> array;
...
for(int i = 0; i < array.size(); i++)
{
...
}

With the sufficient array length, it is possible to measure that accessing a class field is more expensive than using a local variable. One of the reasons is the size() method, which creates a short-lived int on every loop iteration. In fact, every read-only method (returns an object that must not be modified, consequently makes use of the new keyword) in Java will create a new object. In addition, function calls might have an extra cost too. Do not panic, the previous code can be easily optimized for the scenarios where the length stays immutable within the loop by caching the array length value:

ArrayList<Foo> array;
...
final int length = array.size();

for(int i = 0; i < length; i++)
{
...
}

Object pooling

To minimize the garbage collector workload, we can reuse objects whose mission has finished. In order to understand the real potential of this subject, think of a shooter game where bullets appear and disappear from the screen continuously. Creating new instances every frame can be disastrous for the garbage collector overhead and that is why object pooling will just reuse the unused instances by resetting and reactivating them.

Fortunately, Libgdx comes with an out-of-the-box pooling mechanism that applies to any class defined by the programmer, for instance, Bullet. Perform the following steps:

  1. The first step is to make it implement the Poolable interface. This will request for the reset() method to be overridden, so it gets called whenever the object is freed in order to leave it ready to be reused. It means that this is the place where fields must be set to default values and object references to null:
    public class Bullet implements Poolable {
       private Vector2 position;
       private boolean visible;
    ...
       public void reset() {
    position.set(0,0);
    visible = false;
    }
    }
  2. Now, we need to create a Pool instance to store the references to our Bullet objects. As you can see in the next code snippet, it is necessary to specify the way to construct a new object, so we will just make use of its constructor:
    private final Pool<Bullet> bulletPool = new Pool<Bullet>() {
    protected Bullet newObject() {
    return new Bullet();
    }
    };

The way to interact with the Pool class comes defined by five methods:

  • obtain(): This returns a new or freed object
  • free(T object): This puts the object back in the pool if not full (containing the same number of instances than the value set to its max public field)
  • freeAll(Array<T> objects): This puts all the instances contained in objects back in the pull if not full, ignoring the null ones
  • clear(): This gets rid of all objects within the pool
  • getFree(): This returns the number of available instances within the pool

It is good to know the basis of object pooling but there is a shortcut class called Pools, that you might want to explore in order to dynamically create a specific pool instance so the process is summarized in a single line of code making use of reflection:

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

Note

Java has its own Reflection API which is able to examine and make modifications to the code itself at runtime by using introspection. For more information refer to http://docs.oracle.com/javase/7/docs/api/java/lang/reflect/package-summary.html.

Note that the garbage collector problem is mainly an Android (Dalvik Virtual Machine) issue. For other platforms, it might actually be counter-productive, as pooling increases the number of references, which makes garbage collecting more expensive on other platforms than it would be when creating a new object each time. However, if your memory needs are great, pooling would save you from having a lot of garbage collection which can be worth the hassle on all platforms. In conclusion, the best way always is to profile so you can draw true conclusions.

There's more…

You are perfectly capable of minimizing garbage collector usage by following the previous bunch of tips.

The other way to reduce the generated garbage is by using StringBuffer instead of the + concatenation operator. So instead of doing it this way:

"Libgdx Cross Platform" + "Game Development" + "Cookbook"

You can do it as follows and save some valuable resources:

StringBuffer sb = new StringBuffer();
Sb.append("Libgdx Cross Platform").append("Game Development").append("Cookbook");

However, if you want to dive a little bit more into the topic, do not hesitate to take a look at the Google I/0 2009 conference about Writing Real-Time Games for Android by Chris Pruett at the following link:

https://www.youtube.com/watch?v=U4Bk5rmIpic

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.191.234.150