×
Community Blog All You Need to Know About Upgrading from JDK 8 to JDK 11

All You Need to Know About Upgrading from JDK 8 to JDK 11

This article describes the process of upgrading from JDK 8 to JDK 11, the enhancements after the upgrade, and new features in JDK 11.

By Linxing

1

1. Background

Why do we need to upgrade to JDK 11?

Performance

The JDK 11 G1 collector has significantly improved its GC performance compared to JDK 8. Both performance metrics and memory usage have seen substantial enhancements. Various industry data indicators also demonstrate that the JDK 11 G1 collector performs exceptionally well in response to burst traffic spikes.

 

Average TGC pause time

Number of YGCs

Number of Major GCs

Total pause time

Throughput

JDK8+CMS

311ms

140

1

43.9s

92.7%

JDK11+CMS

274ms

122

1

34.9s

94.2%

JDK11+G1

177ms

175

1

31.6s

94.7%

Version compatibility

Spring Boot 2.7.x and later versions will no longer support Java 8 as the minimum version. Spring Boot 2.6.x is the last main-line version that officially supports Java 8. Some new middleware and components also no longer support JDK 8.

Inevitable trend

JDK 11 (LTS) has become mainstream within the industry and is widely accepted and used in the Java development community and industry.

2

2. All You Need to Know Before Upgrading

• JDK 11 has introduced significant changes and is not backward compatible. Therefore, the more complex your business code and the more extensive the chains that are called, the more challenging the upgrade will be. You will encounter many compatibility issues, such as the second-party package does not support the new version of JDK.

• JDK 11 has removed some APIs that have been marked as obsolete in Java 8, such as sun.misc.Unsafe methods, so your upgrade may also involve code changes.

• Verification is a long and time-consuming process, and many issues may only appear during runtime, so you need to validate the overall system functionality to ensure system stability.

3. Upgrade Process

Upgrade locally to get your JDK 11 running.

• Download JDK 11 locally

It will not be discussed in detail here. Pay attention to distinguishing between the arm version and the x64 version of JDK.

• Select JDK 11 on IDEA

3

• Upgrade the framework

Modify the POM file

<maven.compiler.target>11</maven.compiler.target>
<maven.compiler.source>11</maven.compiler.source>
<java.version>11</java.version>
<spring-boot.version>2.1.6.RELEASE</spring-boot.version>
<lombok.version>1.18.12</lombok.version>

Software

Minimum version

spring-boot

2.1.x begins to provide support for JDK 11

spring

5.1.x

idea

2018.2

maven

3.5.0

lombok

1.18.x

netty

Upgrade it to version 4.1.33.Final or later, otherwise, it will cause off-heap memory usage.

apache common lang3

3.12.0

JDK 11 has removed it, and manual dependency on second-party libraries is required.

<dependency>
    <groupId>javax.xml.soap</groupId>
    <artifactId>javax.xml.soap-api</artifactId>
    <version>1.4.0</version>
</dependency>
<dependency>
    <groupId>com.sun.xml.ws</groupId>
    <artifactId>jaxws-ri</artifactId>
    <version>2.3.3</version>
    <type>pom</type>
</dependency>
<dependency>
    <groupId>com.sun.xml.bind</groupId>
    <artifactId>jaxb-impl</artifactId>
    <version>2.3.0</version>
</dependency>
<dependency>
    <groupId>javax.xml.bind</groupId>
    <artifactId>jaxb-api</artifactId>
    <version>2.3.0</version>
</dependency>
<dependency>
    <groupId>javax.annotation</groupId>
    <artifactId>javax.annotation-api</artifactId>
    <version>1.3.2</version>
</dependency>
<dependency>
    <groupId>com.sun.activation</groupId>
    <artifactId>javax.activation</artifactId>
    <version>1.2.0</version>
</dependency>
<dependency>
    <groupId>com.sun.xml.bind</groupId>
    <artifactId>jaxb-core</artifactId>
    <version>2.3.0</version>
</dependency>
<dependency>
    <groupId>com.alibaba.jvm</groupId>
    <artifactId>java-migration-jdk-patch</artifactId>
    <version>0.3.1</version>
    <type>pom</type>
</dependency>
<dependency>
    <groupId>javax.transaction</groupId>
    <artifactId>javax.transaction-api</artifactId>
    <version>1.2</version>
</dependency>

• Existing issues

Deprecated: A global security auto-configuration is now provided

4

In Spring Boot 2.0 and above versions, this configuration item has been deprecated and removed. If you want to disable endpoint security, you need to configure the Actuator endpoints in the Spring Security configuration. This configuration item enables security detection by default.

Dependency 'org.hibernate:hibernate-validator:' not found

5

You need to specify a version number.

<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-validator</artifactId>
    <version>6.2.4.Final</version>
</dependency>

Applications cannot be remotely debugged

Cause

In JDK 8, the JDWP (Java Debug Wire Protocol) bound host/IP to 0.0.0.0 by default. For security reasons, this was changed to localhost (127.0.0.1) in JDK 9 and later versions. Therefore, if developers only specify the port in the debug configuration, remote debugging can't run after the upgrade.

Solution

When specifying debug options, set the host/IP to *, for example:

agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:8000

Or 0.0.0.0, such as:

agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=0.0.0.0:8000

Other issues

(1) maven-compiler-plugin: It is recommended to upgrade this plugin to the latest version. Additionally, specify the version in the parent POM and in the POM of each module where you need to ensure a specific version (such as a JDK 8 version package meant for others). Use the following configuration:

<maven.compiler.source>11</maven.compiler.source> <maven.compiler.target>11</maven.compiler.target>

(2) Spring Boot and Spring versions: Spring supports JDK 11 since version 5.1, and Spring Boot supports JDK 11 since 2.1.X. We recommend you upgrade to the latest available versions.

(3) Netty should be upgraded to version 4.1.33 or above due to issues with the release of off-heap memory.

(4) Since Lombok inserts its own compilation logic at compile time, after upgrading to JDK 11, you need to upgrade Lombok to the latest version (the latest version at the time of writing is 1.18.24).

(5) Most applications may need to upgrade Spring or Spring Boot. Please ensure that thorough regression testing is performed.

(6) The security-spring-boot-starter version is 1.x. x or 2.x. x, corresponding to Spring Boot1 or Spring Boot2. Please upgrade to 2.x. x.

Application Deployment and Release

• Use the G1 garbage collector

Remove
#SERVICE_OPTS="${SERVICE_OPTS} -XX:+UseConcMarkSweepGC -XX:+UseCMSCompactAtFullCollection -XX:CMSMaxAbortablePrecleanTime=5000"
#SERVICE_OPTS="${SERVICE_OPTS} -XX:+CMSClassUnloadingEnabled -XX:CMSInitiatingOccupancyFraction=80 -XX:+UseCMSInitiatingOccupancyOnly"
#SERVICE_OPTS="${SERVICE_OPTS} -XX:+ExplicitGCInvokesConcurrent -Dsun.rmi.dgc.server.gcInterval=2592000000 -Dsun.rmi.dgc.client.gcInterval=2592000000"
#SERVICE_OPTS="${SERVICE_OPTS} -XX:ParallelGCThreads=4"
#SERVICE_OPTS="${SERVICE_OPTS} -Xloggc:${MIDDLEWARE_LOGS}/gc.log -XX:+PrintGCDetails -XX:+PrintGCDateStamps"

SERVICE_OPTS="${SERVICE_OPTS} -XX:+UseG1GC -XX:+UseVtableBasedCHA -XX:+UseCompactObjectHeaders"
SERVICE_OPTS="${SERVICE_OPTS} -XX:G1HeapRegionSize=8m"
SERVICE_OPTS="${SERVICE_OPTS} -XX:+G1BarrierSkipDCQ"
SERVICE_OPTS="${SERVICE_OPTS} -Xlog:gc*:/home/admin/logs/gc.log:time"
SERVICE_OPTS="${SERVICE_OPTS} -XX:G1HeapWastePercent=2"
SERVICE_OPTS="${SERVICE_OPTS} -XX:+ExplicitGCInvokesConcurrent -Dsun.rmi.dgc.server.gcInterval=2592000000 -Dsun.rmi.dgc.client.gcInterval=2592000000"
if [ -n "$AJDK_MAX_PROCESSORS_LIMIT" ]; then
    SERVICE_OPTS="${SERVICE_OPTS} -XX:ActiveProcessorCount=$AJDK_MAX_PROCESSORS_LIMIT"
fi

GC tuning notes (data source: JVM team)

Typically, the G1 GC is a garbage collector with defaults that enable it to work efficiently without modification. The old, standard Java GC tuning experiences do not apply.

-Xmn parameters generally require no additional configuration.

G1 has preset values for -XX:NewSize and -XX:MaxNewSize (they are not the same), and it will calculate and set the size of the young generation for each GC based on actual runtime conditions to control GC pauses.

-XX:NewRatio requires no additional configuration.

-XX:SurvivorRatio generally requires no additional configuration.

Generally, most users are not clear about the meaning of this parameter or its impact on GC. G1 adaptively processes the GC behavior related to this parameter.

Important parameters after upgrading G1

-XX:MaxGCPauseMillis=N (A target value for desired maximum pause time in milliseconds.)

The default value is 200 ms. Many users tend to set it lower, but in most cases, this doesn't make much of a difference. The actual G1 GC pause tasks will not decrease with shorter pause times. In contrast, a smaller target value can lead to more frequent GCs, which will affect throughput. Generally, you do not need to set this parameter. For better throughput, it is usually set larger to ensure that the young generation does not decrease too much. You can also consult JVM experts to consider adjusting -XX:NewSize and -XX:MaxNewSize to maintain the size of the young generation and appropriate throughput performance.

-XX:InitiatingHeapOccupancyPercent=N -XX:-G1UseAdaptiveIHOP (Commonly used in e-commerce core applications.)

These two parameters are usually used together. JDK 11 has introduced G1UseAdaptiveIHOP to improve the utilization of the old generation (large heaps, typically several tens of GB or more than 100 GB). In medium-sized heaps (usually 5-20 GB) commonly used in our container specifications, we sometimes encounter issues that old GC or young GC occurs too frequently. Therefore, you need to set a suitable static IHOP (the heap occupancy threshold for the old generation to trigger a marking cycle).

-XX:G1HeapRegionSize (E-commerce core applications typically use 8 MB to 32 MB.)

This parameter sets the size of a G1 region to address GC exceptions caused by humongous objects (any object that is more than half a heap region size, occupying one or more regions). The default heap region size is Heap size/2048. If the default value is too small, excessive allocation of humongous objects can easily cause To-space exhausted pause time exceptions.

[2024-01-05T14:14:31.817+0800] GC(266) To-space exhausted

-XX:G1HeapWastePercent (The default value is 5, and some e-commerce core applications set it to 2.)

When collecting old regions, the mixed GC cycle will not be initiated when the reclaimable percentage is less than the 5% heap size to reduce the pause time of mixed GCs. For example, when Xmx is 10G and the heap waste percentage is 5%, 500 MB can be wasted, which is a waste for the Java heap. Therefore, you can reduce the heap space waste by setting this parameter to 2. It is not recommended to set it to 0, as this can significantly increase the pause time of mixed GCs.

-XX:G1MixedGCCountTarget (The target number of mixed GCs, and the default value is 8.)

The actual number of mixed GCs usually falls short of the G1MixedGCCountTarget. If the concurrent marking and mixed GC cycles are infrequent and the pause time of a single mixed GC is too long, you can typically consider increasing this parameter to 16, for example, to distribute the workload of a single mixed GC pause, thereby reducing the pause time.

FAQ about upgrading G1

After upgrading from CMS to G1, memory usage in containers and Java processes has increased

Many applications observe an overall increase in memory usage in containers and Java processes after upgrading to JDK 11, primarily due to differences in heap usage. The old generation in CMS is non-compacting and controlled by CMSInitiatingOccupancyFraction, which determines the occupancy percentage to trigger GC. Therefore, shortly after application startup, the heap's old generation usage does not rise quickly. In contrast, G1's heap regions are loosely managed, utilizing the entire heap, which makes memory usage appear higher. The essence of this issue is heap utilization. CMS initially reserves a portion of the heap that is not used. This issue can be solved by lowering the Xmx parameter (it is also used by some e-commerce core applications).

Pauce exceptions caused by To-space exhausted in GC logs

The majority are caused by the allocation of large objects, frequently appearing in GC logs.

Pause Young (Concurrent Start) (G1 Humongous Allocation)

Excessive allocation of large objects quickly fills up heap space, leading to To-space exhausted/evacuation failure in GC, which requires additional pause time to handle and even more time-consuming full GC for complete heap compaction.

Frequent GCs

For the traditional CMS/Parallel GC, the size of the young generation is fixed. However, the size of the young generation in G1 is automatically adjusted. To meet pause time requirements, the young generation size might be reduced, leading to more frequent GCs. Generally, it's recommended to avoid a too-small MaxGCPauseMillis parameter as the description mentioned above. You can also increase the configuration of the MaxGCPauseMillis, and consult a Q&A expert if necessary to adjust -XX:NewSize and -XX:MaxNewSize.

Excessively long mixed GC pauses

In addition to the young GC which collects and cleans up young generation objects, G1 also performs mixed GCs after the concurrent mark, which includes collecting old generation objects. Therefore, mixed GC usually has a longer pause time. If the pause time of a single mixed GC is too long, consider increasing the G1MixedGCCountTarget parameter mentioned above to further distribute the old generation object compaction tasks and reduce the pause time.

4. Enhancements After the Upgrade

Daily Operation

6

It can be seen that in daily operation, the G1 GC time consumption has been greatly reduced.

Effect of Stress Testing

TPS20 under the same stress testing conditions

7
8

It can be clearly seen that the GC time consumption has significantly reduced, with speeds increased by about 70%.

9

Online Operation

10

As can be seen from the figure, the time consumption of YGC is significantly reduced and the performance is nearly improved by 50%. This is attributed to the generational collection capability.

YGC average pause time

Number of YGCs

Effect

JDK8+CMS

7.4ms

10347

 

JDK11+G1

3.74ms

10649

Performance is improved by 49.5%

 

5. New Features in JDK 11

Enhancements to strings in JDK 11

String str = " i am lzc ";
boolean isblank = str.isBlank();         //Determine whether the string is blank
boolean isempty = str.isEmpty();         //Determine whether the string is empty
String  result1 = str.strip();           //Remove leading and trailing whitespace
String  result2 = str.stripTrailing();  //Remove trailing whitespace
String  result3 = str.stripLeading();   //Remove leading whitespace
String  copyStr = str.repeat(2);        //Copy the string several time
long  lineCount = str.lines().count();  //Count the number of rows

System.out.println(isblank);            //Result:false            
System.out.println(isempty);            //Result:false 
System.out.println(result1);            //Result:i am lzc 
System.out.println(result2);            //Result: i am lzc 
System.out.println(result3);            //Result:i am lzc  
System.out.println(copyStr);            //Result: i am lzc  i am lzc  
System.out.println(lineCount);          //Result:1  

Enhancements to Files methods in JDK 11

Path filePath = Files.writeString(Path.of("/temp/a.txt"), "Sample text");
String fileContent = Files.readString(filePath);
System.out.println(fileContent.equals("Sample text"));

Enhancements to Stream methods in JDK 11

//Stream allows accepting a null value and returns 0 when calculating count.
long count = Stream.ofNullable(null).count();
System.out.println(count); // 0


//Methods take a predicate to decide which elements to drop from the stream.
//Simple words: remove elements from the collection that meet the condition until they no longer do.
List list1 = Stream.of(1, 2, 3, 2, 1)
        .dropWhile(n -> n < 3)
        .collect(Collectors.toList());
System.out.println(list1); // [3, 2, 1]

//Methods take a predicate to decide which elements to select from the stream.
//Simple words: extract elements from the collection that meet the condition until they no longer do.
List list2 = Stream.of(1, 2, 3, 2, 1)
        .takeWhile(n -> n < 3)
        .collect(Collectors.toList());
System.out.println(list2); // [1, 2]

Enhancements to collection methods in JDK 11 (such as List and Map)

List list1 = List.of(1, 3, 5, 7);
List list2 = List.copyOf(list1);
System.out.println(list2); //Result: [1,3,5,7]

Map<Integer, String> map1 = Map.of(1, "a", 2, "b", 3, "c");
Map<Integer, String> map2 = Map.copyOf(map1);
System.out.println(map2); //Result: {1=a, 2=b, 3=c}

Enhancements to optional methods in JDK 11

//New method orElseThrow throws an exception if the value is absent.
Object v2 = Optional.ofNullable(null).orElseThrow();      //Result: An exception is thrown.

//New method ifPresentOrElse executes the first callback function if not null, and executes the second if null.
Optional.ofNullable(null).ifPresentOrElse(
        (x) -> {
            System.out.println("Data:" + x);
        }, () -> {
            System.out.println("Data is not found");
        });

//Provide another Optionals as a callback for empty Optionals.
Object v3 = Optional.ofNullable(null)
        .or(() -> Optional.of("fallback"))
        .get();   //Result:fallback
System.out.println(v3);

HTTP Client

HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
    .uri(URI.create(uri))
    .build();
// Asynchronous
client.sendAsync(request, HttpResponse.BodyHandlers.ofString())
    .thenApply(HttpResponse::body)
    .thenAccept(System.out::println)
    .join();

// Synchronous
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.body());


Disclaimer: The views expressed herein are for reference only and don't necessarily represent the official views of Alibaba Cloud.

0 1 0
Share on

Alibaba Cloud Community

1,042 posts | 256 followers

You may also like

Comments