Which JVM flag selects HotSpot's Parallel garbage collector, how many collector threads does it use by default, and was it ever the JVM's own default choice?
answer
- -XX:+UseParallelGC
- 1 thread per CPU up to 8, then tapers
- -XX:ParallelGCThreads to pin, esp. many JVMs per host
- Default through JDK 8; G1 from JDK 9
- Verify with -XX:+PrintFlagsFinal
basics
~20 s-XX:+UseParallelGC selects it. Thread count defaults to the number of available processors up to 8, then grows more slowly above that, and is overridable with -XX:ParallelGCThreads. It was the default on server-class machines through JDK 8; from JDK 9 the region-based G1 collector became the default.
solid answer
~50 s**Selecting it:** `-XX:+UseParallelGC`. In modern JDKs that single flag gives you a parallel young collector *and* a parallel old-generation mark-compact; the separate `-XX:+UseParallelOldGC` flag was deprecated in JDK 14 and removed in JDK 15, so you should not see it in new configurations. **Thread count:** by default the JVM derives it from the processors it believes it has — one thread per processor up to 8, then a fraction of the additional processors beyond that. Override with `-XX:ParallelGCThreads=N`. Since JDK 10 the JVM is container-aware, so inside a container it reads the CPU quota rather than the host's core count; before that it saw the host and could badly over-provision GC threads in a small container. **Default status:** Parallel was the default collector on server-class machines through JDK 8. JDK 9 made the region-based G1 collector the default. On very small machines the JVM still selects the Serial collector instead. Verify rather than assume: `java -XX:+PrintFlagsFinal -version | grep -E 'UseParallelGC|ParallelGCThreads'`.
code
text · 5 lines# select it, pinning the GC thread count for a dense host
java -XX:+UseParallelGC -XX:ParallelGCThreads=4 -Xms2g -Xmx2g -jar app.jar
# confirm what the JVM actually decided on this machine/container
java -XX:+PrintFlagsFinal -version | grep -E 'UseParallelGC|UseG1GC|ParallelGCThreads|MaxHeapSize'go deeper
Recall the flag and that the collector uses several GC threads; know that the default collector changed after JDK 8.
Add the thread-count formula and the ParallelGCThreads override, and know that the separate old-generation flag is gone.
Bring the operational angle: pinning thread counts on dense hosts, container-awareness from JDK 10, and verifying with PrintFlagsFinal.
Treat collector and thread-pool sizing as fleet configuration — explicit, version-pinned launch profiles rather than reliance on ergonomics that changes between JDK releases.
## Turning it on `-XX:+UseParallelGC` is the whole answer for selection. Two details matter around it. First, in current JDKs this one flag gets you parallelism on **both** sides of the heap: a parallel copying collector for the young generation and a parallel mark-summary-compact for the old generation. Historically these were separate — `-XX:+UseParallelOldGC` had to be added to parallelise the old-generation collection — but that became the default long ago, and the flag itself was deprecated in JDK 14 and removed in JDK 15. If you see it in a configuration file, it is a fossil worth deleting. Second, collector flags are mutually exclusive in practice; specifying two collectors is a configuration error, not a merge. Set exactly one. ## How many GC threads The default is derived from the number of processors the JVM believes are available: - Up to 8 processors: one GC thread per processor. - Above 8: 8 threads plus a fraction (five eighths) of the processors beyond the eighth. So a 32-processor machine gets roughly 23 threads rather than 32. The taper exists because GC threads have coordination overhead and diminishing returns; throwing every core at a small young generation adds synchronisation cost without shortening the pause. Override with `-XX:ParallelGCThreads=N`. Reasons to do so: - **Many JVMs on one host.** Each JVM sizes its GC thread pool from the *machine's* processors, so ten JVMs on a 32-core box can each spin up ~23 GC threads. During a collection they trample each other. Pinning a smaller number per JVM is standard practice on dense hosts. - **Very small heaps.** A tiny young generation collected by 16 threads spends more time coordinating than collecting. ## The container trap Before JDK 10, the JVM read the *host's* CPU count even when running inside a container with a CPU quota of, say, one core. The consequences were straightforward and bad: a GC thread pool sized for a 64-core host inside a 1-core container, contending on a fraction of one CPU, plus a default heap sized from host memory. From JDK 10 onward the JVM is container-aware by default (`-XX:+UseContainerSupport`), reading the cgroup CPU quota and memory limit, and sizes both the GC thread pool and the default heap accordingly. If you are running an older JDK in containers, set `-XX:ParallelGCThreads` and the heap explicitly. ## Was it ever the default? Yes. Through JDK 8, the JVM's ergonomics selected the Parallel collector automatically on any machine it classified as **server-class** — historically at least two processors and at least around 2 GB of memory. On smaller machines it selected the Serial collector. JDK 9 changed the server-class default to the region-based G1 collector, reflecting the shift toward long-lived services with multi-gigabyte heaps and latency objectives. The Serial collector is still chosen automatically on small machines. So a great deal of legacy production tuning advice on the internet silently assumes Parallel, because on JDK 8 you got it without asking. This is why an interviewer asking "what collector are you running?" is really asking whether you know your JDK version, since the answer changes with it. ## Verify, never assume Two commands settle any argument: - `java -XX:+PrintFlagsFinal -version | grep -E 'UseParallelGC|UseG1GC|ParallelGCThreads|MaxHeapSize'` prints the values ergonomics actually resolved on *this* machine or container. - `-Xlog:gc` on the running process shows the collector's own log lines; Parallel's lines are all `Pause Young`/`Pause Full` with no `Concurrent` entries. Getting into the habit of reading the resolved flags rather than trusting the command line is the single most useful operational reflex in this area, because ergonomics, container limits and configuration-management layering all conspire to produce something other than what you thought you set. ## What to say "`-XX:+UseParallelGC`. Threads default to one per processor up to 8 and taper above that; `-XX:ParallelGCThreads` overrides, and I pin it when several JVMs share a host. It was the JDK 8 server-class default; JDK 9 moved the default to G1. And I confirm with `-XX:+PrintFlagsFinal` rather than assume."
- Why would you ever lower -XX:ParallelGCThreads below the default?Mainly when several JVMs share one host. Each JVM sizes its GC thread pool from the machine's processor count, so ten JVMs on a 32-core box each want roughly twenty GC threads; when two of them collect at once they contend badly. Pinning a smaller number per JVM keeps collections predictable. The other case is a very small heap, where coordination overhead among many threads outweighs the parallel speed-up.
- You run a JDK 8 JVM in a container limited to one CPU. What goes wrong?Before JDK 10 the JVM reads the host's CPU count and memory rather than the cgroup limits, so it may classify the machine as server-class, select the Parallel collector, size a GC thread pool for dozens of cores, and pick a default maximum heap from host memory. All of that then runs inside one CPU's worth of quota. The remedy on old JDKs is to set the collector, -XX:ParallelGCThreads and the heap bounds explicitly.
saying these in an interview costs you the question
- Believing the Parallel collector is still the JVM default on modern JDKs
- Still adding -XX:+UseParallelOldGC, which was removed in JDK 15
- Assuming GC threads equal the core count on large machines — the count tapers above eight
- Expecting container CPU limits to be respected on JDKs older than 10
- Reading the command line instead of the resolved flags to determine what the JVM is actually running