By Tang Yueping (Lingli)
Spring6 and SpringBoot3 launched in 2022. Before that, no matter how many new versions came out, most people were unwilling to upgrade and stuck to Java 8. This time, Spring came up with a big move. The lowest dependency of SpringBoot3 and Spring6 is JDK17! It skips JDK 8-16 and upgrades directly to JDK 17. So, why JDK 17?
There are so many new versions of JDK. Why did Spring choose JDK 17?
The main reason is that it is a commercially free LTS version announced by Oracle. LTS means Long Term Support, which is officially guaranteed to be supported for a long time.
The preceding figure shows the timeline of Oracle JDK support. As you can see, JDK 17 will be supported up to September 2029. According to the speed of technology update iteration, free commercial use for eight years is a good intention, so users can safely upgrade JDK to JDK 17. (Also, JDK 8 will be supported for a longer period that can be extended to December 2030.)
Since the creation of JDK, JDK 7, JDK 8, JDK 11, and JDK 1 have been supported long-term. JDK 17 will be the most important LTS version since Java8. It is the result of eight years of efforts by the Java community.
Java8 has been the pain point in the Java community. Java8 provides many features, such as Lambda expression and Optional class. Also, Java8 has a long support time. These are the reasons why it can be used as yet. It represents a tug-of-war between stability-oriented corporate management and developers that embrace changes. No upgrades! It becomes the tacit choice of major companies. Now, that balance may be broken. Java's dominant framework, SpringBoot chooses the smallest supported Java LTS version, which is the latest Java17.
So, let's see, from JDK8 to JDK17, what are the results of the Java community's eight years of hard work?
Its important features are mainly API optimization (such as Client API supporting HTTP2) and JVM using G1 as the default garbage collector.
Its important features are local variable type inference is realized through the var keyword, making Java language a weak type language and G1 garbage collection of JVM changing from single thread to multi-thread parallel processing, thus reducing G1 pause time.
Its important features are improvements for JDK9 and JDK10, mainly for stream, collection, and other APIs and a new ZGC garbage collector.
Its important features are the switch expression syntax extension, G1 collector optimization, and new Shenandoah GC garbage collection algorithm.
Its important features are ZGC optimization, releasing memory back to the operating system, and introducing NIO to the underlying socket implementation.
JDK16 is equivalent to the formal introduction of some features of JDK14 and JDK15, such as instanceof pattern matching, record introduction, etc. JDK16 became the final version.
Although JDK17 is an LTS version, it does not introduce prominent features (like JDK8 and JDK11), mainly integrating and improving the previous versions.
Java Platform Module System (JPMS) is the highlight of the Java 9 release. It is known as the Jigshaw Project. Modules are new structures, just like the packages we already have. An application developed with the new modular programming can be viewed as a collection of interacting modules with well-defined boundaries and dependencies between them.
JPMS supports writing modular applications and modularizing JDK source code. JDK 9 comes with about 92 modules (which can be changed in the GA version). The Java 9 Module System has a java.base module. It is called the basic module. It is an independent module and does not depend on any other modules. All other modules rely on java.base by default.
In Java modular programming:
module-info.class
under the root directorymodulepath
instead of the classpath
. The modular jar file added to the classpath is a normal jar file, and the module-info.class
file will be ignored.A typical module-info.java
class is shown below:
module helloworld {
exports com.alibaba.eight;
}
module test {
requires helloworld;
}
Summary: The purpose of modularization is to enable JDK components to be split, reused, replaced, and rewritten. For example, if you are unsatisfied with Java’s GUI, you can implement a GUI yourself. If you are unsatisfied with Java syntax, you can replace javac with other languages and compilers for other languages, such as kotlin and kotlinc. Without modularization, it is almost difficult to implement. Each time you modify a module, it is unfeasible for you to recompile the entire JDK and release the entire SDK. Modularization can help realize effective customization and deployment.
In versions prior to Java 10, when we wanted to define local variables, we need to provide an explicit type on the left side of the assignment and an implementation type on the right side of the assignment.
MyObject value = new MyObject();
In Java 10, local variable type inference is provided, and variables can be declared by var.
var value = new MyObject();
Local variable type inference will introduce the var keyword without explicitly specifying the type of the variable.
The local variable type inference is the syntactic sugar that Java 10 provides to developers.
Although we use var to define in the code, the virtual machine does not know this var. In the process of compiling java files into class files, the real type of variables will be used to replace var.
Java has been using HttpURLConnection
for HTTP communication for a long time. Over time, the requirements became complex, and the requirements of the application became demanding. Prior to Java 11, developers had to resort to feature-rich libraries, such as Apache HttpComponents or OkHttp.
We see that the Java 9 release includes a HttpClient
implementation as an experimental feature. It has evolved to the final feature of Java 11. Now, Java applications can communicate over HTTP without any external dependencies.
As a new HTTP connector officially launched in JDK11, the supported functions are new. The main features are listed below:
HTTP2.0 is also supported by other clients, while HttpClient uses CompletableFuture as asynchronous return data. WebSocket support is an advantage of HttpClient. The support of responsive streams is a major advantage of HttpClient.
The NIO model, functional programming, CompletableFuture asynchronous callbacks, and responsive streams in HttpClient equip HttpClient with strong concurrent processing capabilities, so its performance is high and its memory footprint is less.
The teeling collector has been exposed as a static method Collectors::teeing. The collector forwards its input to the other two collectors and combines their results with the function.
Example:
List<Student> list = Arrays.asList(
new Student("A", 55),
new Student("B", 60),
new Student("C", 90));
// Average Score and Total Score
String result = list.stream().collect(Collectors.teeing(
Collectors.averagingInt(Student::getScore),
Collectors.summingInt(Student::getScore),
(s1, s2) -> s1 + ":" + s2));
// The Lowest Score and the Highest Score
String result2 = list.stream().collect(Collectors.teeing(
Collectors.minBy(Comparator.comparing(Student::getScore)),
Collectors.maxBy(Comparator.comparing(Student::getScore)),
(s1, s2) -> s1.orElseThrow() + ":" + s2.orElseThrow()
));
System.out.println(result);
System.out.println(result2);
List<String> list = Arrays.asList("1", "2", "3");
// Wrote this before.
List<Integer> oneList = list.stream()
.map(Integer::parseInt)
.collect(Collectors.toList());
// Now write like this.
List<Integer> twoList = list.stream()
.map(Integer::parseInt)
.toList();
This change extends the switch statement so it can be used as a statement or expression. Instead of defining a statement to break each case block, we can simply use the arrow syntax:
boolean isWeekend = switch (day) {
case MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY -> false;
case SATURDAY, SUNDAY -> true;
default -> throw new IllegalStateException("Illegal day entry :: " + day);
};
int size = 3;
String cn = switch (size) {
case 1 -> "One";
case 2 -> "Two";
case 3, 4 -> "Three";
default -> "Unknown";
};
System.out.println(cn);
// To use this preview feature, we must explicitly instruct the JVM with the-enable-preview flag during application startup.
With yield, we can efficiently return values from switch expressions and implement the strategy pattern easily.
public class SwitchTest {
public static void main(String[] args) {
var me = 4;
var operation = "Square";
var result = switch (operation) {
case "Double" -> {
yield me * 2;
}
case "Square" -> {
yield me * me;
}
default -> me;
};
System.out.println(result);
}
}
Earlier, to embed JSON in our code, we declared it as a string literal.
String json = "{\r\n" + "\"name\" : \"lingli\",\r\n" + "\"website\" : \"https://www.alibaba.com/\"\r\n" + "}";
Now, let's write the same JSON.
String json = """
{
"name" : "Baeldung",
"website" : "https://www.alibaba.com/"
}
""";
There is no need to escape double quotes or add carriage returns. After using text blocks, it is easier to write, read, and maintain embedded JSON.
isBlank()
: Return true if the string is empty or if the string only contains spaces (including tabs). Note: isEmpty()
only returns true if the length is 0.lines()
: Split a string into a stream of strings, each containing one line.strip()
: From the beginning and endStripLeading()/stripTrailing()
only removes spaces at the beginning and the end.repeat(int times)
: Return a string that takes the original string and repeats the string a specified number of times.readString()
: It allows you to read strings directly from file paths.writeString(Path path)
: Write a string to a file at a specified pathindent(int level)
: The Specified Amount of Indentation in the String Negative values only affect the leading whitespace.transform(Function f)
: Apply the given lambda to a stringBefore:
Object obj = "Dayang";
if (obj instanceof String) {
String t = (String) obj;
// TODO
}
Now:
Object obj = "Dayang";
if (obj instanceof String t) {
// TODO At this time, t is already of the String type.
}
Traditional Java applications create a class, instantiate the class through this creating method, and access or set the value of a member variable through getter and setter methods. With the record keyword, your code becomes more concise.
/**
* record class
* You can overwrite equals() hashCode() toString() method without writing get and set.
* @author DAYANG
*/
record User(String name, Integer age) {
@Override
public String toString() {
return "User[" +
"name='" + name + '\'' +
", age=" + age +
']';
}
@Override
public boolean equals(Object obj) {
return false;
}
@Override
public int hashCode() {
return 0;
}
}
JDK9: Set G1 as JVM default garbage collector.
JDK10: Parallel full garbage collector G1, which improves the latency of G1 through parallel Full GC. The implementation of full GC for G1 uses the single-thread-purge-compression algorithm. JDK 10 began using the parallelize-purge-compression algorithm.
JDK11: A new generation of ZGC garbage collectors (experimental) is launched. The goal is that GC pause time will not exceed 10ms, which can handle both hundreds of megabytes and several terabytes.
JDK14: Remove the CMS garbage collector, deprecate the garbage collection algorithm combination of ParallelScavenge + SerialOld GC, and port the ZGC garbage collector to the macOS and Windows platforms.
JDK 15: ZGC (JEP 377) and Shenandoah (JEP 379) are no longer experimental features. The default GC is still G1.
JDK16: Enhance ZGC. ZGC gets 46 enhancements and 25 bug fixes with no more than 10ms of controlling stw time.
In terms of throughput, there is not much difference between JDK 8 and JDK 11 in Parallel, and JDK 17 is about 15% higher than JDK 8. JDK 17 in G1 is 18% higher than JDK 8. ZGC was introduced in JDK 11, and JDK 17 improved by more than 20% compared with JDK 11.
In terms of GC latency, the improvement in JDK 17 is pronounced. We can see that efforts to reduce GC pauses have paid off, and many of the improvements are due to GC improvements.
In Parallel, JDK 17 improves by 40% compared with JDK 8 and JDK 11. In G1, JDK 11 improves by 26% compared with JDK 8, and JDK 17 improves by nearly 60% compared with JDK 8. Compared with JDK 11, JDK 17 in ZGC improves by more than 40%.
We can see that the ZGC in JDK 17 is well below the target: sub-millisecond pause time. The G1's goal is to maintain a balance between latency and throughput, far below its default target: 200 milliseconds of pause time. ZGC is designed to ensure that the pause time does not change with the size of the heap, and we can see what happens when the heap is expanded to 128GB. From a pause time perspective, G1 is better at handling larger heaps than Parallel since it can guarantee that the pause time meets a specific goal.
The figure above compares the peak native memory usage of three different collectors. Since both Parallel and ZGC are stable from this perspective, we should take a look at the raw numbers. We can see that G1 has improved in this area, mainly because all functions and enhancements have improved the efficiency of memory set management.
Summary: No matter which collector is used, the overall performance of the JDK 17 has been improved compared to the old version. In JDK 8, Parallel was the default setting, but it was changed to G1 in JDK 9. Since then, the G1 has improved faster than Parallel, but in some cases, Parallel may still be the best choice. The addition of ZGC (officially used by JDK 15) has become a third high-performance alternative.
Prior to Java 15, all classes that could inherit from other classes without restriction could implement a public interface unless the inherited class was declared final.
Now in Java 15, a class or interface can be declared as a sealed class or interface with the sealed modifier to restrict its inheriting classes.
/**
* Define an abstract sealed class Pet. Its implementation classes can only be Dog or Cat. Other implementation classes are not allowed.
*/
public abstract sealed class Pet
permits Dog, Cat {}
final class Dog extends Pet {
}
final class Cat extends Pet {
}
// Sealed classes and interfaces restrict other classes or interfaces from extending or implementing them.
public sealed interface Shape{
final class Planet implements Shape {}
final class Star implements Shape {}
final class Comet implements Shape {}
}
public abstract sealed class Test{
final class A extends Test {}
final class B extends Test {}
final class C extends Test {}
}
Edwards-Curve Digital Signature Algorithm (EdDSA) is another additional digital signature scheme added in Java 15 with JEP 339. It provides better performance and secure signatures compared to other available signature schemes.
Judging from the development trend, the project loom function has been previewed in JDK19. It can be found that many Java tools in this version have begun to upgrade for loom. Project loom will be officially launched in JDK21, which is a long-term supported version. Please look forward to it.
Various servlet containers, as well as jetty, netty, vert.x, etc., found the corresponding upgrade label in their latest version of the release note, saying they have added certain support. The most important is loom (or virtual thread support). We predict that once JDK21 is released, more software will be developed!
The upgrade of JDK is an inevitable trend.
Some people may think the upgrade will cause extra work. Considering security risks, they are less likely to upgrade. There are no substantial benefits but risks. However, considering Oracle will no longer maintain JDK8 and Spring will no longer maintain past versions in the future, to keep up with the times and use the latest technology, this will promote the upgrade of JDK.
When more companies use JDK17, more new frameworks in the future will support JDK17 (at minimum) because it is not worth it to only be compatible with the old JDK. When most frameworks, communities, and forums are discussing JDK17 technologies and various problem-solving methods, enterprises will inevitably be pushed to upgrade.
https://zhuanlan.zhihu.com/p/480293185
1,053 posts | 259 followers
FollowAlibaba Cloud Community - March 17, 2023
Alibaba Cloud Community - June 14, 2024
Alibaba Developer - August 8, 2019
digoal - July 4, 2019
Alibaba Cloud Native Community - March 29, 2023
Alibaba Cloud Community - December 4, 2023
1,053 posts | 259 followers
FollowMigrate your legacy Oracle databases to Alibaba Cloud to save on long-term costs and take advantage of improved scalability, reliability, robust security, high performance, and cloud-native features.
Learn MoreExplore Web Hosting solutions that can power your personal website or empower your online business.
Learn MoreA low-code development platform to make work easier
Learn MoreExplore how our Web Hosting solutions help small and medium sized companies power their websites and online businesses.
Learn MoreMore Posts by Alibaba Cloud Community