Tips for reducing frame rate spikes due to JIT early in a game?

For roughly the first 30-40 seconds in my main ‘play game’ state there are a series of severe spikes in the execution time of individual frames. These occur very roughly about 5 seconds apart. Sometimes a spike can cause a frame to be almost 1 second. At about 30 seconds the spikes start to reduce in severity until the game is running with no noticeable issues. (PC is dual core, 3.5Ghz, 64Bit, Java 8, 8 GB RAM).

One severe spike can actually be triggered 100% of the time by clicking on a particular button in the game, but only on the very first click. The spike will not happen again until the game is closed and restarted. I analyzed the code in this button and found nothing that could obviously cause such a big spike.

This made me think that these spikes are due to methods in my code being compiled by the JIT compiler. So using the ASM library I wrote a simple test that parses a JAR containing game class files and writes out the number of bytecode instructions for each method.

The largest method in my main game loop is 2980 instructions while other larger methods are 1891, 1446 and 1037, 1028. I’ve read that Hotspot compiles methods up to about 8000 instructions, so I assume these are being compiled ok.

So I’m wondering if it’s worth the effort of say getting all methods down to a max of 500 instructions or so? I guess that the smaller the methods the faster they can be compiled and therefore a reduction in the severity of spikes? On the other hand, the event handler for the button that causes a spike is only 45 instructions.

I’ve also thought about doing a ‘warmup’ period where I run the full game loop for say 10 seconds but with input disabled / blank display, just to allow the spikes/compilation to occur without annoying the player. Right now, I would actually say that a delay of 10 seconds would be less annoying to the player than having to deal with frame spikes while trying to play the game. Is this a reasonable/common thing to do?

Of course, let me know if you think its not the JIT compiler at all. Thanks.

Do not bother counting byte code instructions and by no means “optimize” code by reducing method sizes based on guesses what the compiler might do.
Use an ordinary profiler to search for suspicious behaviour, bad performance, memory leaks, etc.

@65K

I’ve been using VisualVM for profiling. When the spikes occur the garbage collector was at 0%. The ‘sawtooth’ heap pattern showed reclaiming of memory only every 20 seconds or so but the GC remained at 0% at all times.

I also tried passing Xms and Xmx JVM arguments of 1024 / 2048 to ensure the heap isn’t resized. This worked, VisualVM showed no change to the heap size, but the frame large spikes continued to occur as described above.

I’m not going to try guessing what the compiler might do, but regardless, isn’t it a good idea in Java to have many smaller methods both for code maintenance and performance?

Do you think the spikes are caused by the JIT compiler or something else?

For performance, no. For maintenance, yes, as long as methods aren’t cut into such small pieces that you get the opposite effect: less readability

Nobody can tell without examining your running game.

warm-up spikes can be fixed with JVM start arguments, (custom build ^^)
Try this:


-server
-XX:+DoEscapeAnalysis

+maybe search more in docs

up: it also may be caused by static fields initialization (new static array list 1000) - in middle of frame
can be fixed by calling all - heavy static data classes - in init phase

@Icecore, Thanks, I’ll try the server and escape analysis flags. There are so many to read up on, I guess it’s very game specific as to what will have noticeable effects or not.

Good point about static variable initialization, I’ll have a look at that too.

Try using two-tier compilation with the server VM.

Escape analysys won’t do anything for you.

Cas :slight_smile:

What is two tier compilation for the server VM? I found this (http://docs.oracle.com/javase/7/docs/technotes/guides/vm/performance-enhancements-7.html) but that looks like it is only for the client VM?

client/server VM is just a 32-bit VM thing. Everything’s server VM for 64-bit.

Shouldn’t we go back to the original post and wonder whether all assertions are correct?

A 1000ms JIT pause is highly unlikely, given that the JIT does its work incrementally.

I bet it’s a code path that the OP isn’t expecting, triggering a lot of disk I/O, the incorrect initialisation of a data-structure, etc etc.

If you simply profile your code (and use System.currentTimeMillis / System.nanoTime to confirm your suspicions) you’ll most likely find the culprit quite quickly.