I built a real-time interface to some hardware in Smalltalk 20-years ago! (Yikes!) Since Java and Smalltalk have the same GC theory (at least, simplistically), some of my experiences would translate. I also spent 5-years running high-performance, low(ish) latency stuff in Java too. The simple rules of low-latency Java (or Smalltalk) are:
- Create less garbage. Don't tell me that the GC is an issue when you are creating huge garbage. If your application is running in a tight loop (small amount of code being run a LOT of times), spend time on understanding the GC generational model... create only small temporary objects, and really focus on understanding where every object goes. If you can avoid the stop-the-world gc from kicking in (and you can), then most of the issues go away.
- Force a GC before you go into low-latency land. This was something I could do in Smalltalk 20-years ago... I would force a GC right before I went into time-sensitive stuff.
- Define what you mean by low-latency. I've worked on major systems (100s of millions of financial transactions per day with strict latency requirements) where there was a team dedicated to it, and frankly, Java wasn't nor never would be an option. (In fact, we worked with mainframe operating systems people can't even name anymore!) However, most people don't mean that when they say low-latency.
- Finally, if what you really mean is low-latency measured in 100s of milliseconds, that isn't really hard and Java application servers do that in their sleep now. (I've build business transactional applications that do 100,000 transaction with 700,000 SQL calls per second on some standard software and some beefy hardware) Just work inside the best practices space of these products. It is very easy to say "Java doesn't perform" when your code is crap.