I've been spending my free time working on a small game for the Android Mobile operating system (Currently available on the T-Mobile G1).
Android is a "Managed Operating System", in such that most client applications are actually hosted within a custom Java virtual machine...Because you don't get raw hardware access you have to be very careful about both resource management and optimization, which is something that I (as a application developer), don't usually have to think too heavily about.
Part of this game depends on a simple particle engine to handle "explosions". While developing this, I found that my hardware would bog down every 10 seconds or so once there was a lot of particles on the screen.
After doing some profiling, I realized the slow down was caused by the virtual machines garbage collector cleaning up dead particles.
I realized that I was creating a new object for every particle, and then leaving it for the garbage collector once the particle died.
Something like:
for (int i = 0; i < mParticles.length; i++) {
// Math to update Particle Velocities
if (mParticles[i].Age > MAX_AGE) {
// Particle is dead
mParticles.removeAt(i);
}
}
Once a particle was removed from mParticles, there was no longer any references to it, so eventually the garbage collector would come along and clean up, ruining the game performance while it runs...By eventually I mean every 10 seconds :)
I decided to make a judgment call of performance versus overall memory allocation.
The new version of code creates an array of particles the size of MAX_PARTICLES. It uses this array as a pool to retrieve particles as needed, and if the pool is exhausted, it starts to reuse the oldest particles in the pool.
public void addExplosion (int x, int y, int color) {
for (int i = 0; i < 25; i++) {
if (mPointer == MAX_PARTICLES) {
mPointer = 0;
}
mParticles[mPointer]
.setAge(mRandom.nextInt(255));
mParticles[mPointer]
.setX(x + (mRandom.nextInt(32) - 16));
mParticles[mPointer]
.setY(y + (mRandom.nextInt(32) - 16));
mParticles[mPointer]
.setVelocityX((float)(mRandom.nextInt(6) - 3));
mParticles[mPointer]
.setVelocityY((float)(mRandom.nextInt(5) - 10));
mParticles[mPointer]
.setColor(color);
mPointer++;
}
}
Because each particle has a reference back the particle array, they are NEVER collected, even when not used...Essentially a controlled memory leak. This causes a slightly larger memory overhead but keeps pressure off the heap, preventing constant collection.
This really stabilized the framerate of the game engine, and was a very simple optimization.
Posted by Jonathan Holland on 3/13/2009.