Jon Masamitsu's Weblog

  • Java
    September 29, 2016

A Work in Progress

Guest Author
When you execute "java ..." you get some number of garbage collection (GC) threads. If you want to know how many GC threads, you can execute

java -XX:+PrintFlagsFinal -version

and look for ParallelGCThreads in the output (of which there will be plenty). You will find something like

uintx ParallelGCThreads := 23 {product}

If the number of GC threads you see looks way too big to you, this note may be of some interest.

That number of GC threads is some fraction of the number of hardware threads on your platform. The assumption back in the day was that if you were running on a large machine, you had a large application and would need lots of GC threads. Not as true today.

The problem with having too many GC threads is that they consume resources (they are threads that have thread stacks and take up OS resources) and can get in each others way (many threads trying to do little work can have high parallel execution overhead with little benefit).

If you know your applications does not do much GC work (GC pauses are short and infrequent) then you can just set the number of GC threads on the command line with -XX:ParallelGCThreads=NNN to a small number. Or you can use the serial GC with -XX:+UseSerialGC (which does not do multi-threaded GC so doesn't create multiple GC threads).

If you are not sure about how many GC threads you need, you can try the option -XX:+UseDynamicNumberOfGCThreads (introduced in jdk8). Before each garbage collection the number of GC threads is recalculated based on the amount of heap that is being used and the number of application threads that are running. The flag -XX:HeapSizePerGCThread=XXX controls the number of GC threads per XXX bytes of used Java heap. The default value of HeapSizePerGCThread on a 64bit machine is about 85M. If the number of applications threads is YYY, the GC will choose two times YYY as the number of GC threads. The larger of these two calculations is used as the number of GC threads. I acknowledge that the latter scaling with the number of application threads can be problematic for applications that have lots of threads waiting for requests, but it makes some sense for other applications. And the number of dynamically calculated GC threads never exceeds the number of GC threads if UseDynamicNumberOfGCThreads is off.

Dynamically calculating the number of GC threads to use before each collection avoids the problem of too many GC threads fighting over too little work. The implementation in jdk8 did, however, still create all the GC threads at start up. So hundreds of GC threads could be created at start up (it happens) but many fewer ever used. An improvement already integrated into jdk9 is to start up the GC threads lazily. Before each GC, if the dynamically chosen number of GC threads do not yet all exist, additional threads are created to reach the chosen number.

Turn on -XX:+TraceDynamicGCThreads to see how many GC threads you are getting.

And, finally, why do I entitle this "A Work in Progress"? The number of chosen GC threads can be improved. And the best number of GC threads to use may be different for different parts of a collection. Hopefully, someone will get to those refinements so that more and more you can "Let the VM do it".

Be the first to comment

Comments ( 0 )
Please enter your name.Please provide a valid email address.Please enter a comment.CAPTCHA challenge response provided was incorrect. Please try again.