JDK Upgrade Journey: From JDK 8 to JDK 17 with Performance Gains and GC Tuning
This article documents a comprehensive upgrade from JDK 8 to JDK 17, detailing new language and JVM features, evaluation criteria for selecting services, step‑by‑step migration of Maven settings, dependencies, SpringBoot and middleware, performance testing results, and ZGC‑focused garbage‑collector tuning.
Since JDK 8 was released nine years ago, the author investigates which JDK versions and features are worth adopting and how they can solve current performance bottlenecks.
The new features introduced after JDK 8 are listed, including G1 and ZGC garbage collectors, concurrent API updates (JEP 266), collection factory methods (JEP 269), the modern HTTP client (JEP 321), NPE location improvements (JEP 358), pattern‑matching for instanceof (JEP 394), record classes (JEP 395), switch‑expression enhancements (JEP 361), text blocks (JEP 378), sealed classes (JEP 409) and various low‑level data‑structure optimisations.
Before upgrading, the team defines four evaluation criteria for selecting applications: solving existing problems, having complete regression testing, low technical debt, and deep knowledge of the system and its middleware.
The chosen target is a high‑traffic settlement page built on JDK 8, SpringBoot 2.0.8, and internal middleware (UMP, SGM, DUCC, CDS, JMQ, JSF, R2M). Its characteristics—high response‑time requirements, heavy GC pauses, low technical debt, and simple middleware usage—make it ideal for a JDK 17 migration.
Upgrade steps include:
Update Maven to use a newer JDK version: <java.version>11</java.version> <maven-compiler-plugin.version>3.8.1</maven-compiler-plugin.version> <maven-source-plugin.version>3.2.1</maven-source-plugin.version> <maven-javadoc-plugin.version>3.3.2</maven-javadoc-plugin.version> <maven-surefire-plugin.version>2.22.2</maven-surefire-plugin.version>
Add missing Java EE modules removed after JDK 11: <!-- JAVAX --> <dependency> <groupId>javax.annotation</groupId> <artifactId>javax.annotation-api</artifactId> <version>1.3.1</version> </dependency> <dependency> <groupId>javax.xml.bind</groupId> <artifactId>jaxb-api</artifactId> <version>2.3.0</version> </dependency> ... (other JAXB and activation dependencies)
Upgrade SpringBoot to a version that supports JDK 17 (Spring 5.3 / SpringBoot 2.5‑2.7) and adjust the spring.main.allow-bean-definition-overriding and spring.main.allow-circular-references flags as needed.
Upgrade internal middleware versions (UMP, JSF, JMQ, etc.) and ensure compatible Netty/Javassist versions.
Configure JVM launch parameters for debugging, GC logging, and ZGC: JAVA_DEBUG_OPTS=" -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:8000 " JAVA_GC_LOG_OPTS=" -Xlog:gc*:file=/export/logs/gc.log:time,tid,tags:filecount=10:filesize=10m " JAVA_MEM_OPTS=" -server -Xmx12g -Xms12g -XX:MaxMetaspaceSize=256m -XX:MetaspaceSize=256m -XX:MaxDirectMemorySize=2048m -XX:+UseZGC -XX:ZAllocationSpikeTolerance=3 -XX:ParallelGCThreads=8 -XX:CICompilerCount=3 -XX:-RestrictContended -XX:+AlwaysPreTouch -XX:+ExplicitGCInvokesConcurrent -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/export/logs "
For JDK 11+ add required module opens for internal middleware: if [[ "$JAVA_VERSION" -ge 11 ]]; then SGM_OPTS="$SGM_OPTS --add-opens jdk.management/com.sun.management.internal=ALL-UNNAMED ..." UMP_OPT=" --add-opens java.base/sun.net.util=ALL-UNNAMED" JSF_OPTS=" --add-opens java.base/sun.util.calendar=ALL-UNNAMED --add-opens java.base/java.util=ALL-UNNAMED ..." fi
The performance test compares JDK 8 with G1 against JDK 17 with ZGC:
Version
Throughput
Avg Latency
Max Latency
JDK 8 G1
99.966%
35.7 ms
120 ms
JDK 17 ZGC
99.999%
0.0254 ms
0.106 ms
Results show virtually unchanged throughput (≈+0.01 %) while GC pause time drops by more than 1,400× on average and over 1,100× for the worst case.
GC tuning focuses on ZGC: a large heap (e.g., -Xmx12g -Xms12g ) to accommodate live data, sufficient Metaspace and DirectMemory, and a slightly higher -XX:ZAllocationSpikeTolerance=3 to handle traffic spikes. Additional tweaks such as increasing ParallelGCThreads and CICompilerCount improve throughput at modest CPU cost.
After the migration and tuning, the system runs stably with regular load‑testing cycles and no observed regressions.
The article concludes with a brief summary and a list of recommended readings on JVM internals, database evolution, and performance optimisation.
JD Tech
Official JD technology sharing platform. All the cutting‑edge JD tech, innovative insights, and open‑source solutions you’re looking for, all in one place.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.