I am developing a client for a contest in Java, and whenever I receive a request, I have two seconds to respond. The time after the response until the next request is unknown.
Sometimes, finding the right response takes nearly 2 seconds, and sometimes it's only a matter of a few milliseconds. The issue is when garbage collection happens in one of the longer calculations (which also allocates a lot of objects) right towards the end of the two seconds, and thus the response is sent too late and I get disqualified.
Using verbose gc output I identified that the gc usually takes about 0.6s, even though I tried to limit it lower. I also tried to invoke System.gc()
on the shorter calculations (since I am sure I have about 1.8s where I don't need to do anything), but it took 1-3s, which is not safe either.
My program has very few long-living objects, most live shorter than a second.
I know that the program will always run on the same machine with these resources available:
My current jvm parameters:
java -Dfile.encoding=UTF-8 \
-XX:MaxGCPauseMillis=200 \
-XX:GCPauseIntervalMillis=2050 \
-XX:+UseConcMarkSweepGC -XX:+CMSParallelRemarkEnabled \
-XX:+UseCMSInitiatingOccupancyOnly -XX:CMSInitiatingOccupancyFraction=70 \
-XX:+ScavengeBeforeFullGC -XX:+CMSScavengeBeforeRemark \
-verbose:gc -XX:+PrintGCDetails -XX:+PrintGCTimeStamps
System.gc()
equivalent that only works with the young objects and doesn't check the tenured generation?Here are things that you could try to reduce pauses:
After a lot of experimenting, I found the flags I needed and want to share this knowledge now:
-XX:+UseConcMarkSweepGC
- after comparing log results of G1 and CMS for my usecase I determined t CMS pause times are lower, additionally it still supports single-threaded collections-XX:+ExplicitGCInvokesConcurrent
Upon calling System.gc() not a Full GC will be invoked, but a standard one.-XX:NewRatio=1
the size ratio of old generation to young generation, this is the lowest value since I have very few long-living objects-mx800m
-ms800m
reduces and fixes the memory size, thus collections will occur more frequent and take less time. Trades throughput for responsiveness.-XX:-UseParNewGC
Disables parallelization for collecting the young generation. This was the dealbreaker and reduced the GC stop-the-world times from 0.2-0.5s to 0.02-0.2s, since i only have one core available. (Combining this with CMS is deprecated and subject to be removed in newer Java versions)Using these parameters I could reduce GC pause times from varying between 0.4s and 3s down to consistently less than 0.2 seconds. And for completeness, these flags were the most useful for debugging:
-XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintHeapAtGC -XX:+PrintTenuringDistribution
Please don't forget that these are optimised for very specific requirements: Onle one core available, a lot of young garbage, no old collections, very low stop-the-world-pauses
This is a little cheat sheet I can recommend for further reading: http://blog.ragozin.info/2016/10/hotspot-jvm-garbage-collection-options.html
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With