Java GC Tutorials - Herong's Tutorial Examples - v1.12, by Herong Yang
"-XX:ParallelGCThreads" - # of Threads
This section describes how to use the '-XX:ParallelGCThreads' JVM option to control the number threads to be used by the Parallel Collector.
In the previous tutorial, we learned that the Parallel collector did use 2 parallel threads to perform both Young and Full GCs.
The next question is then how the Parallel collector determines the number of threads to use and any options to control it.
By search Java documentation, I found this description: "On a machine with N hardware threads where N is greater than 8, the parallel collector uses a fixed fraction of N as the number of garbage collector threads. The fraction is approximately 5/8 for large values of N. On selected platforms, the fraction drops to 5/16. At values of N below 8, the number used is N.
But the output from the previous tutorial contradicts what's said in Java documentation. The Parallel collector used 2 parallel threads, however my virtualized Windows 10 computer has 4 cores and 8 logical processors. The Parallel collector should use 8 threads.
If you want control the number of parallel treads explicitly, you can use the "-XX:ParallelGCThreads=n" JVM option. In the example below, I ran the test program with 4 parallel threads:
herong> java -Xms40m -Xmx80m -XX:+UseParallelGC \ -Xlog:gc=debug,gc+heap=info,gc+task+time=debug \ -XX:ParallelGCThreads=4 GarbageCollection [info][gc] Using Parallel [info][gc,heap] GC(0) PSYoungGen: 10240K->1504K(11776K) [info][gc,heap] GC(0) ParOldGen: 0K->1843K(27648K) [info][gc] GC(0) Pause Young (Allocation Failure) 10M->3M(38M) 4.245ms [debug][gc,task,time] GC(0) VM-Thread 861668 869434 870054 [debug][gc,task,time] GC(0) GC-Thread 0 entries: 3 [debug][gc,task,time] GC(0) [ noop task 861773 861773 ] [debug][gc,task,time] GC(0) [ thread-roots-task 861803 867733 ] [debug][gc,task,time] GC(0) [ steal-task 867733 868332 ] [debug][gc,task,time] GC(0) GC-Thread 1 entries: 10 [debug][gc,task,time] GC(0) [ scavenge-roots-task 861794 861842 ] [debug][gc,task,time] GC(0) [ thread-roots-task 861852 861884 ] [debug][gc,task,time] GC(0) [ thread-roots-task 861884 861886 ] [debug][gc,task,time] GC(0) [ thread-roots-task 861886 861890 ] [debug][gc,task,time] GC(0) [ thread-roots-task 861890 861892 ] [debug][gc,task,time] GC(0) [ thread-roots-task 861892 861893 ] [debug][gc,task,time] GC(0) [ thread-roots-task 861894 862264 ] [debug][gc,task,time] GC(0) [ scavenge-roots-task 862265 862271 ] [debug][gc,task,time] GC(0) [ scavenge-roots-task 862271 864296 ] [debug][gc,task,time] GC(0) [ steal-task 864297 868355 ] [debug][gc,task,time] GC(0) GC-Thread 2 entries: 4 [debug][gc,task,time] GC(0) [ scavenge-roots-task 861805 861836 ] [debug][gc,task,time] GC(0) [ thread-roots-task 861845 862009 ] [debug][gc,task,time] GC(0) [ scavenge-roots-task 862010 866709 ] [debug][gc,task,time] GC(0) [ steal-task 866709 869320 ] [debug][gc,task,time] GC(0) GC-Thread 3 entries: 9 [debug][gc,task,time] GC(0) [ thread-roots-task 861818 861964 ] [debug][gc,task,time] GC(0) [ thread-roots-task 861989 861990 ] [debug][gc,task,time] GC(0) [ thread-roots-task 861991 861993 ] [debug][gc,task,time] GC(0) [ thread-roots-task 861993 861994 ] [debug][gc,task,time] GC(0) [ scavenge-roots-task 861994 861995 ] [debug][gc,task,time] GC(0) [ scavenge-roots-task 861995 861997 ] [debug][gc,task,time] GC(0) [ scavenge-roots-task 861998 863620 ] [debug][gc,task,time] GC(0) [ steal-task 863621 868332 ] [debug][gc,task,time] GC(0) [ waitfor-barrier-task 868333 869363 ]
The output confirms that 4 threads were used by the Parallel collector for Young GCs.
Continue to GC(3):
[debug][gc] GC(3) Expanding ParOldGen from 27648K by 27136K to 54784K [info ][gc,heap ] GC(3) PSYoungGen: 1520K->0K(25600K) [info ][gc,heap ] GC(3) ParOldGen: 25943K->25253K(54784K) [info ][gc ] GC(3) Pause Full (Ergonomics) 26M->24M(78M) 17.253ms [debug][gc,task,time] GC(3) VM-Thread 7386871 7400301 7420455 [debug][gc,task,time] GC(3) GC-Thread 0 entries: 4 [debug][gc,task,time] GC(3) [ noop task 7386888 7386888 ] [debug][gc,task,time] GC(3) [ mark-from-roots-task 7386971 7390691 ] [debug][gc,task,time] GC(3) [ steal-marking-task 7390691 7393648 ] [debug][gc,task,time] GC(3) [ steal-region-task 7400577 7418907 ] [debug][gc,task,time] GC(3) GC-Thread 1 entries: 5 [debug][gc,task,time] GC(3) [ noop task 7386870 7386870 ] [debug][gc,task,time] GC(3) [ thread-roots-marking-task 7386982 ... ] [debug][gc,task,time] GC(3) [ steal-marking-task 7391065 7393634 ] [debug][gc,task,time] GC(3) [ steal-region-task 7400571 7418883 ] [debug][gc,task,time] GC(3) [ waitfor-barrier-task 7418886 7418943 ] [debug][gc,task,time] GC(3) GC-Thread 2 entries: 4 [debug][gc,task,time] GC(3) [ noop task 7386901 7386902 ] [debug][gc,task,time] GC(3) [ thread-roots-marking-task 7386989 ... ] [debug][gc,task,time] GC(3) [ steal-marking-task 7388408 7393642 ] [debug][gc,task,time] GC(3) [ steal-region-task 7400560 7418900 ] [debug][gc,task,time] GC(3) GC-Thread 3 entries: 22 [debug][gc,task,time] GC(3) [ noop task 7386914 7386914 ] [debug][gc,task,time] GC(3) [ mark-from-roots-task 7386959 7387128 ] [debug][gc,task,time] GC(3) [ thread-roots-marking-task 7387129 ... ] [debug][gc,task,time] GC(3) [ thread-roots-marking-task 7387152 ... ] [debug][gc,task,time] GC(3) [ thread-roots-marking-task 7387153 ... ] [debug][gc,task,time] GC(3) [ thread-roots-marking-task 7387154 ... ] [debug][gc,task,time] GC(3) [ thread-roots-marking-task 7387785 ... ] [debug][gc,task,time] GC(3) [ thread-roots-marking-task 7387787 ... ] [debug][gc,task,time] GC(3) [ thread-roots-marking-task 7387789 ... ] [debug][gc,task,time] GC(3) [ thread-roots-marking-task 7387802 ... ] [debug][gc,task,time] GC(3) [ thread-roots-marking-task 7387804 ... ] [debug][gc,task,time] GC(3) [ thread-roots-marking-task 7387807 ... ] [debug][gc,task,time] GC(3) [ thread-roots-marking-task 7387808 ... ] [debug][gc,task,time] GC(3) [ mark-from-roots-task 7387810 7387810 ] [debug][gc,task,time] GC(3) [ mark-from-roots-task 7387811 7387813 ] [debug][gc,task,time] GC(3) [ mark-from-roots-task 7387813 7387995 ] [debug][gc,task,time] GC(3) [ mark-from-roots-task 7387996 7388012 ] [debug][gc,task,time] GC(3) [ mark-from-roots-task 7388012 7388013 ] [debug][gc,task,time] GC(3) [ mark-from-roots-task 7388013 7388014 ] [debug][gc,task,time] GC(3) [ steal-marking-task 7388014 7393629 ] [debug][gc,task,time] GC(3) [ waitfor-barrier-task 7393629 7393662 ] [debug][gc,task,time] GC(3) [ steal-region-task 7400476 7418883 ]
The output confirms that 4 threads were used by the Parallel collector for Full GCs.
Conclusion: the Parallel collector may not detect the number cores/logical processors correctly on your Virtualized Windows computer to calculate the number of threads to use. You should use "-XX:ParallelGCThreads" JVM option to specify the number of threads to use by the Parallel collector.
Table of Contents
Heap Memory Area and Size Control
JVM Garbage Collection Logging
Introduction of Garbage Collectors
Serial Collector - "+XX:+UseSerialGC"
►Parallel Collector - "+XX:+UseParallelGC"
Parallel Collector GC Log Message Format
Log Message Types from Parallel Collector
"--Xlog:gc+task+time=debug" - Print GC Threads
►"-XX:ParallelGCThreads" - # of Threads
Parallel Collector Stops Application for Minor/Major GC
PSYoungGen Collector Using Tenuring Age
Parallel Collector Changing NewRatio and SurvivorRatio
Parallel Collector Adaptive Size Policy
Adaptive Size Policy Log Messages
"-Xlog:gc+ergo=trace" - Minor GC Report
Adaptive Size Policy Changed Survivor Space
Adaptive Size Policy Changed Eden Space
Adaptive Size Policy for Best Latency
Parallel Collector Stopped using Young Generation
Adaptive Size Policy for Best Throughput
Concurrent Mark-Sweep (CMS) Collector - "+XX:+UseConcMarkSweepGC"
Garbage First (G1) Collector - "+XX:+UseG1GC"
The Z Garbage Collector (ZGC) - "+XX:+UseZGC"
Object References and Garbage Collection
Garbage Collection Performance Test Program
Performance Tests on Serial Collector
Performance Tests on Parallel collector
Performance Tests on Concurrent collector
Performance Tests on G1 collector