-
-
Notifications
You must be signed in to change notification settings - Fork 690
Writing GC friendly Code
With all modern tools, languages and compilers you would think it is easy to write efficient code with low footprint. Well, it is to a certain extent, but if you want a stable application with high performance and low memory consumption, there are still some things that require programmer's attention when coding.
Note: tips, tricks and code snippets used here do not necessarily apply to all types of software development. These primarily target game development and should only be used after profiling your game application. In fact, some of these techniques will produce rather unreadable and ugly-looking code, so be warned.
Before going deep into the optimized code we are going to briefly talk about why we should bother with garbage collection (GC) in the first place. Then, in later sections we'll have a look at some coding techniques that will help us in minimizing GC.
This is not a tutorial on Java memory types, so I'll be brief. If you are unfamiliar with the terminology, it is best if you do some research in the area first and then come back to the tutorial. Searching for "Java stack vs heap" should provide you with sufficient material.
Recall, in a Java application whenever the new keyword is used, an object will be allocated on the heap memory, provided that there is memory to allocate. Once all references to that object go out of scope, the object is marked for GC and the memory it occupies will be freed on the next GC run. There is nothing inherently wrong with GC or how it works. However, the GC call is a relatively high-cost operation. While this is perfectly fine for normal applications, which typically don't load the CPU all the time, the GC calls might cause performance issues in games, which tend to utilize the CPU during most of the application lifetime. Furthermore, real-time games run at 60 frames per second. That is any object allocation times 60. Now imagine this is being done in a loop. If the loop cycles N times during a frame, then that's already N * 60. As you can see, this can easily get out of hand without proper control. This may not affect desktop games as much as mobile games, but the issue still exists.
Now back to FXGL. Ideally, we don't want GC to run at all (while the user is playing that is, we still want GC to clean up the previous level and other stuff we don't need). This is possible only theoretically - just don't create new objects and, voila, the problem is gone. In real-world settings we rely on many other libraries. For example, we use JavaFX as our rendering framework. Therefore, any rendering calls that allocate new objects will eventually trigger GC. However, we can still make changes to our code to minimize the overall need for GC.
Consider the following for-each loop:
Point2D velocity = ...
for (GameEntity enemy : enemies) {
enemy.translate(velocity);
}At first glance, it seems absolutely normal. However, recall that the for-each loop is replaced with the standard iterator loop and after the replacement looks like this:
Point2D velocity = ...
for (Iterator<GameEntity> it = enemies.iterator(); it.hasNext(); ) {
GameEntity enemy = it.next();
enemy.translate(velocity);
}Depending on the underlying implementation iterator() is likely to produce a new iterator on each call. Here's how ArrayList::iterator() is implemented:
public Iterator<E> iterator() {
return new Itr();
}TODO: foreach, forEach
TODO: pools