- Loading...
Shenandoah is an ultra-low pause time garbage collector that reduces GC pause times by performing more garbage collection work concurrently with the running Java program. CMS and G1 both perform concurrent marking of live objects. Shenandoah adds concurrent compaction, which means its pause times are no longer proportional to the size of the heap. Garbage collecting a 100 GB heap or a 2 GB heap has the same predictable pause behavior.
Shenandoah is available in source and binary forms in JDK 8u, 9, and 10 flavors, see the "Build and Run" section for more details.
Shenandoah is the regionalized collector, it maintains the heap as the collection of regions.
The regular Shenandoah GC cycle looks like this:
GC(3) Pause Init Mark 0.771ms GC(3) Concurrent marking 76480M->77212M(102400M) 633.213ms GC(3) Pause Final Mark 1.821ms GC(3) Concurrent cleanup 77224M->66592M(102400M) 3.112ms GC(3) Concurrent evacuation 66592M->75640M(102400M) 405.312ms GC(3) Pause Init Update Refs 0.084ms GC(3) Concurrent update references 75700M->76424M(102400M) 354.341ms GC(3) Pause Final Update Refs 0.409ms GC(3) Concurrent cleanup 76244M->56620M(102400M) 12.242ms
The phases above do roughly this:
Root set includes: thread local variables, references embedded in generated code, interned Strings, references from classloaders (e.g. static final references), JNI references, JVMTI references. Having larger root set generally means longer pauses with Shenandoah, see below for diagnostic techniques.
This section describes the approaches to test and diagnose performance behaviors with Shenandoah.
Basic configuration and command line options:
It is almost always a good idea to run with -Xlog:gc -Xlog:gc+ergo -Xlog:gc+stats for testing. This summary table conveys important information about GC performance, and we would almost inevitably ask for one in a performance bug report. Heuristics logs are useful to figure out GC outliers.
Other recommended JVM options are:
Heuristics tell when Shenandoah starts the GC cycle, and regions it deems for evacuation. Heuristics can be selected with -XX:ShenandoahGCHeuristics=<name>. Some heuristics accept configuration parameters, which might help to tailor the GC operation to your use case better. Available heuristics include:
-XX:ShenandoahInitFreeThreshold=#: Initial remaining free threshold
-XX:ShenandoahMinFreeThreshold=#: Minimum remaining free threshold
-XX:ShenandoahMaxFreeThreshold=#: Maximum remaining free threshold
-XX:ShenandoahHappyCyclesThreshold=#: How many successful marking cycles before improving free threshold
-XX:ShenandoahGarbageThreshold=#: Sets the percentage of garbage a region need to contain before it can be marked for collection.
-XX:ShenandoahFreeThreshold=#: Set the percentage of free heap at which a GC cycle is started
-XX:ShenandoahAllocationThreshold=#: Set percentage of memory allocated since last GC cycle before a new GC cycle is started
continuous. This heuristics runs GC cycles continuously, starting the next cycle as soon as previous cycle finishes, as long as allocations happen. This heuristics would normally incur throughput overheads, but shall provide the most prompt space reclamation. It is often a good idea to cap the number of concurrent GC threads with -XX:ConcGCThreads=<...> when using this heuristics.
In some cycles, Update References phase is merged with Concurrent Marking phase, at heuristics discretion. You can forcefully enable/disable Update References with -XX:ShenandoahUpdateRefsEarly=[on|off].
Approaches to performance analysis:
Many throughput differences can be explained by GC barriers overhead. When running with -XX:ShenandoahGCHeuristics=passive, and that heuristics only, barriers are not required for correctness, and so heuristics disables them. It is then possible to enable the barriers selectively back, and see what barriers are affecting throughput performance. The list of barriers that "passive" heuristics is disabling is listed in GC output, like this:
$ java -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=passive -Xlog:gc [0.002s][info][gc] Passive heuristics implies -XX:-ShenandoahSATBBarrier by default [0.002s][info][gc] Passive heuristics implies -XX:-ShenandoahKeepAliveBarrier by default [0.002s][info][gc] Passive heuristics implies -XX:-ShenandoahWriteBarrier by default [0.002s][info][gc] Passive heuristics implies -XX:-ShenandoahReadBarrier by default [0.002s][info][gc] Passive heuristics implies -XX:-ShenandoahStoreValReadBarrier by default [0.002s][info][gc] Passive heuristics implies -XX:-ShenandoahCASBarrier by default [0.002s][info][gc] Passive heuristics implies -XX:-ShenandoahAcmpBarrier by default [0.002s][info][gc] Passive heuristics implies -XX:-ShenandoahCloneBarrier by default [0.003s][info][gc] Using Shenandoah
--with-native-debug-symbols=internal
, this will get you the mapping to C++ codeperf record java ...
(plain profile) or perf record -g java ...
(call tree profile)perf report
"a"
on the method usually gives a more detailed disassembly for it
It is important to understand that GC pauses might not be the only significant contributor to response times in regular applications. Having large GC pause spells the problem with response time with a very high probability, but the absence of long GC pauses does not always mean decent response time. Queueing delays, network latencies, other services latencies, OS scheduler jitter, etc. could be the contributing cost. Running Shenandoah with response time measurement is recommended to get the full picture of what is going on in the system, which can then be used to correlate with GC pause time statistics.
For example, this is a sample report with jHiccup on one of the workloads:
This section describes the ways one can diagnose and/or debug Shenandoah.
Basic configuration and command line options:
Approaches for debugging:
Bug reports are welcome at the mailing list.
Shenandoah is under active development outside of JDK master releases. It will hopefully enter mainline OpenJDK builds under JEP 189 in JDK 10 timeframe. That said, there are downstream builds available for current JDKs, which one can try today. The changes flow between the development repos and builds as described on a simplified diagram below. If you are an early adopter, trying a bleeding edge should be more profitable performance-wise, but may risk exposure to not-yet-discovered bugs. If you are looking at running Shenandoah in the actual deployments, using the most stable builds is preferred.
More detailed, there are different ways to get Shenandoah:
This would guarantee you run the latest and greatest version. Adding --enable-debug to configure would produce the "fastdebug" build. Some features and bugfixes may not be available in older JDK versions. Older JDK versions are supposed to be more stable.
# JDK 10: $ hg clone http://hg.openjdk.java.net/shenandoah/jdk10 shenandoah # JDK 9: $ hg clone http://hg.openjdk.java.net/shenandoah/jdk9 shenandoah # JDK 8u: $ hg clone http://hg.openjdk.java.net/shenandoah/jdk8u shenandoah $ cd shenandoah/ # Configure and build, JDK 10: $ sh ./configure $ make images # Configure and build, JDK 8u, 9: $ sh ./get_source.sh $ sh ./configure $ make images # Run! JDK 10, 9: $ build/linux-x86_64-normal-server-release/images/jdk/bin/java -XX:+UseShenandoahGC -Xlog:gc [...][info][gc] Using Shenandoah # Run! JDK 8u: $ build/linux-x86_64-normal-server-release/images/j2sdk-image/bin/java -XX:+UseShenandoahGC -version openjdk version "1.8.0-internal" OpenJDK Runtime Environment (build 1.8.0-internal-shade_2016_12_19_15_52-b00) OpenJDK 64-Bit Server VM (build 25.71-b00, mixed mode)
In all cases for building from source it is optional, but advisable to run the tests. This is especially important on platforms beyond what Shenandoah currently targets, and/or building with too new or too old toolchains. You will need jtreg to run the tests, and it makes sense to run test against fastdebug build first:
# Download and unpack jtreg from https://adopt-openjdk.ci.cloudbees.com/job/jtreg/lastSuccessfulBuild/artifact/ # Hook up jtreg to the build: $ sh ./configure --with-jtreg=<jtreg folder> --with-debug-level=fastdebug $ sh ./configure --with-jtreg=<jtreg folder> --with-debug-level=release # Run the tests: $ CONF=linux-x86_64-normal-server-fastdebug make images test TEST="hotspot_gc_shenandoah" $ CONF=linux-x86_64-normal-server-release make images test TEST="hotspot_gc_shenandoah"
There are (nightly/weekly) development builds available at these locations:
In some Linux distributions, Shenandoah is available within the OpenJDK binaries.
Fedora 24+ OpenJDK builds include Shenandoah:
$ cat /etc/redhat-release Fedora release 26 (Twenty Six) $ java -XX:+UseShenandoahGC -version openjdk version "1.8.0_131" OpenJDK Runtime Environment (build 1.8.0_131-b12) OpenJDK 64-Bit Server VM (build 25.131-b12, mixed mode)
Shenandoah is an ultra-low pause time garbage collector that reduces GC pause times by performing more garbage collection work concurrently with the running Java program. CMS and G1 both perform concurrent marking of live objects. Shenandoah adds concurrent compaction, which means its pause times are no longer proportional to the size of the heap. Garbage collecting a 100 GB heap or a 2 GB heap has the same predictable pause behavior.
Shenandoah is available in source and binary forms in JDK 8u, 9, and 10 flavors, see the "Build and Run" section for more details.
Shenandoah is the regionalized collector, it maintains the heap as the collection of regions.
The regular Shenandoah GC cycle looks like this:
GC(3) Pause Init Mark 0.771ms GC(3) Concurrent marking 76480M->77212M(102400M) 633.213ms GC(3) Pause Final Mark 1.821ms GC(3) Concurrent cleanup 77224M->66592M(102400M) 3.112ms GC(3) Concurrent evacuation 66592M->75640M(102400M) 405.312ms GC(3) Pause Init Update Refs 0.084ms GC(3) Concurrent update references 75700M->76424M(102400M) 354.341ms GC(3) Pause Final Update Refs 0.409ms GC(3) Concurrent cleanup 76244M->56620M(102400M) 12.242ms
The phases above do roughly this:
Root set includes: thread local variables, references embedded in generated code, interned Strings, references from classloaders (e.g. static final references), JNI references, JVMTI references. Having larger root set generally means longer pauses with Shenandoah, see below for diagnostic techniques.
This section describes the approaches to test and diagnose performance behaviors with Shenandoah.
Basic configuration and command line options:
It is almost always a good idea to run with -Xlog:gc -Xlog:gc+ergo -Xlog:gc+stats for testing. This summary table conveys important information about GC performance, and we would almost inevitably ask for one in a performance bug report. Heuristics logs are useful to figure out GC outliers.
Other recommended JVM options are:
Heuristics tell when Shenandoah starts the GC cycle, and regions it deems for evacuation. Heuristics can be selected with -XX:ShenandoahGCHeuristics=<name>. Some heuristics accept configuration parameters, which might help to tailor the GC operation to your use case better. Available heuristics include:
-XX:ShenandoahInitFreeThreshold=#: Initial remaining free threshold
-XX:ShenandoahMinFreeThreshold=#: Minimum remaining free threshold
-XX:ShenandoahMaxFreeThreshold=#: Maximum remaining free threshold
-XX:ShenandoahHappyCyclesThreshold=#: How many successful marking cycles before improving free threshold
-XX:ShenandoahGarbageThreshold=#: Sets the percentage of garbage a region need to contain before it can be marked for collection.
-XX:ShenandoahFreeThreshold=#: Set the percentage of free heap at which a GC cycle is started
-XX:ShenandoahAllocationThreshold=#: Set percentage of memory allocated since last GC cycle before a new GC cycle is started
continuous. This heuristics runs GC cycles continuously, starting the next cycle as soon as previous cycle finishes, as long as allocations happen. This heuristics would normally incur throughput overheads, but shall provide the most prompt space reclamation. It is often a good idea to cap the number of concurrent GC threads with -XX:ConcGCThreads=<...> when using this heuristics.
In some cycles, Update References phase is merged with Concurrent Marking phase, at heuristics discretion. You can forcefully enable/disable Update References with -XX:ShenandoahUpdateRefsEarly=[on|off].
Approaches to performance analysis:
Many throughput differences can be explained by GC barriers overhead. When running with -XX:ShenandoahGCHeuristics=passive, and that heuristics only, barriers are not required for correctness, and so heuristics disables them. It is then possible to enable the barriers selectively back, and see what barriers are affecting throughput performance. The list of barriers that "passive" heuristics is disabling is listed in GC output, like this:
$ java -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=passive -Xlog:gc [0.002s][info][gc] Passive heuristics implies -XX:-ShenandoahSATBBarrier by default [0.002s][info][gc] Passive heuristics implies -XX:-ShenandoahKeepAliveBarrier by default [0.002s][info][gc] Passive heuristics implies -XX:-ShenandoahWriteBarrier by default [0.002s][info][gc] Passive heuristics implies -XX:-ShenandoahReadBarrier by default [0.002s][info][gc] Passive heuristics implies -XX:-ShenandoahStoreValReadBarrier by default [0.002s][info][gc] Passive heuristics implies -XX:-ShenandoahCASBarrier by default [0.002s][info][gc] Passive heuristics implies -XX:-ShenandoahAcmpBarrier by default [0.002s][info][gc] Passive heuristics implies -XX:-ShenandoahCloneBarrier by default [0.003s][info][gc] Using Shenandoah
ShenandoahVerifier::verify_oop_fwdptr(obj, oop(BrooksPointer::get_raw(obj)));or perf record -g java ...
It is important to understand that GC pauses might not be the only significant contributor to response times in regular applications. Having large GC pause spells the problem with response time with a very high probability, but the absence of long GC pauses does not always mean decent response time. Queueing delays, network latencies, other services latencies, OS scheduler jitter, etc. could be the contributing cost. Running Shenandoah with response time measurement is recommended to get the full picture of what is going on in the system, which can then be used to correlate with GC pause time statistics.
For example, this is a sample report with jHiccup on one of the workloads:
This section describes the ways one can diagnose and/or debug Shenandoah.
Basic configuration and command line options:
Approaches for debugging:
Bug reports are welcome at the mailing list.
Shenandoah is under active development outside of JDK master releases. It will hopefully enter mainline OpenJDK builds under JEP 189 in JDK 10 timeframe. That said, there are downstream builds available for current JDKs, which one can try today. The changes flow between the development repos and builds as described on a simplified diagram below. If you are an early adopter, trying a bleeding edge should be more profitable performance-wise, but may risk exposure to not-yet-discovered bugs. If you are looking at running Shenandoah in the actual deployments, using the most stable builds is preferred.
More detailed, there are different ways to get Shenandoah:
This would guarantee you run the latest and greatest version. Adding --enable-debug to configure would produce the "fastdebug" build. Some features and bugfixes may not be available in older JDK versions. Older JDK versions are supposed to be more stable.
# JDK 10: $ hg clone http://hg.openjdk.java.net/shenandoah/jdk10 shenandoah # JDK 9: $ hg clone http://hg.openjdk.java.net/shenandoah/jdk9 shenandoah # JDK 8u: $ hg clone http://hg.openjdk.java.net/shenandoah/jdk8u shenandoah $ cd shenandoah/ # Configure and build, JDK 10: $ sh ./configure $ make images # Configure and build, JDK 8u, 9: $ sh ./get_source.sh $ sh ./configure $ make images # Run! JDK 10, 9: $ build/linux-x86_64-normal-server-release/images/jdk/bin/java -XX:+UseShenandoahGC -Xlog:gc [...][info][gc] Using Shenandoah # Run! JDK 8u: $ build/linux-x86_64-normal-server-release/images/j2sdk-image/bin/java -XX:+UseShenandoahGC -version openjdk version "1.8.0-internal" OpenJDK Runtime Environment (build 1.8.0-internal-shade_2016_12_19_15_52-b00) OpenJDK 64-Bit Server VM (build 25.71-b00, mixed mode)
In all cases for building from source it is optional, but advisable to run the tests. This is especially important on platforms beyond what Shenandoah currently targets, and/or building with too new or too old toolchains. You will need jtreg to run the tests, and it makes sense to run test against fastdebug build first:
# Download and unpack jtreg from https://adopt-openjdk.ci.cloudbees.com/job/jtreg/lastSuccessfulBuild/artifact/ # Hook up jtreg to the build: $ sh ./configure --with-jtreg=<jtreg folder> --with-debug-level=fastdebug $ sh ./configure --with-jtreg=<jtreg folder> --with-debug-level=release # Run the tests: $ CONF=linux-x86_64-normal-server-fastdebug make images test TEST="hotspot_gc_shenandoah" $ CONF=linux-x86_64-normal-server-release make images test TEST="hotspot_gc_shenandoah"
There are (nightly/weekly) development builds available at these locations:
In some Linux distributions, Shenandoah is available within the OpenJDK binaries.
Fedora 24+ OpenJDK builds include Shenandoah:
$ cat /etc/redhat-release Fedora release 26 (Twenty Six) $ java -XX:+UseShenandoahGC -version openjdk version "1.8.0_131" OpenJDK Runtime Environment (build 1.8.0_131-b12) OpenJDK 64-Bit Server VM (build 25.131-b12, mixed mode)