In this chapter, we will explore the newly implemented, time-based versioning system for the Java platform. We will survey the current Java landscape with a specific focus on changes introduced with Java 9, 10 (18.3), and 11 (18.9). Our exploration will include an overview of Java 9's modularity, Java Shell, external process control, garbage collection, Java Microbenchmark Harness (JMH), and more. For Java 10, we will highlight key changes to include local variable type inference, Java Development Kit (JDK) consolidation, garbage collection, application class-data sharing (CDS), root certificates, and more. Finally, we will explore changes introduced in Java 11, including dynamic class-file constants, garbage collection, local variable type inference for Lambdas, and more.
Things we will learn by the end of this chapter include:
- Understanding the Java platform's new versioning model
- Understanding the significance of Java 9
- Benefiting from changes introduced with Java 10
- Benefiting from changes introduced with Java 11
This chapter and subsequent chapters feature Java 11. The Standard Edition (SE) of the Java platform can be downloaded from Oracle's official download site (http://www.oracle.com/technetwork/java/javase/downloads/index.html).
An integrated development environment (IDE) software package is sufficient. IntelliJ IDEA, from JetBrains, was used for all coding associated with this chapter and subsequent chapters. The Community version of IntelliJ IDEA can be downloaded from the site (https://www.jetbrains.com/idea/features/).
The first version of Java was released as Java 1 in 1996. Since then, there have been several incremental releases, each of which followed a feature-driven release model. Starting with Java 10, Oracle has implemented a new, time-based release model. In this section, we will look at the original model to provide a foundation to show how the Java platform evolved, and look at the new versioning model and why it matters.
Following the 1996 launch of Java 1, subsequent releases had the nomenclature of 1.1, 1.2, 1.3, and 1.4. With the release of 1.5, the Java platform was referred to as Java 5. Frequent updates were released for Java 5 until Java 6 was released followed by Java 7, Java 8, and Java 9.
The following table provides a condensed view of the Java release history until Java 9:
|Release Name||Version||Year Released||Code Name|
|Java 1.1||1.1||1997||(Abigail, Brutus, Chelsea)|
|Java 9||9||2017||*Code names no longer used|
The release of Java 9 was a significant change to the Java platform and how each of the versions was numbered. With the post-Java 9 releases, Oracle decided to abandon their feature-based model, opting for a time-released model instead.
Java 9 was released in 2017 and two releases were scheduled for 2018. Those releases were Java 10 and Java 11. The version numbers for these post-Java 9 releases followed the YY.M format. So, with Java 10 released in March 2018, the version number was 18.3. Java 11, released in September 2018, has a version number of 18.9.
The general premise behind the new time-based release model is to have releases scheduled predictably and frequently. Here are the details:
- Feature releases: Issued every six months (each March and September)
- Update releases: Issued every quarter
- Long-term support release: Issued every three years
There are great gains to be had, from a developer's perspective, with this model. Developers no longer need to wait long for releases to the Java platform. More significant is the fact that no release will represent a major change to the platform of the kind that Java 9 was.
Unarguably, the modularization of the Java platform, developed as part of Project Jigsaw, was the greatest change introduced to the Java platform with Java 9. Initially planned for Java 8, but postponed, Project Jigsaw was one of the main reasons why the final release of Java 9 was itself postponed. Jigsaw also introduced a few notable changes to the Java platform and was one of the reasons Java 9 was considered a major release. We will explore these features in detail in subsequent chapters.
Apart from the Jigsaw-related Java enhancement proposals, there is a long list of other enhancements that made it in Java 9. This section explores the most significant features introduced in Java 9, specifically:
- Breaking the monolith
- Using the Java Shell
- Taking control of external processes
- Boosting performance with G1
- Measuring performance with Java Microbenchmark Harness (JMH)
- Getting ready for HTTP 2.0
- Encompassing reactive programming
Over the years, the utilities of the Java platform have continued to evolve and increase, making it into one big monolith. In order to make the platform more suitable for embedded and mobile devices, the publication of stripped-down editions, such as Java Connected Device Configuration (CDC) and Java Micro Edition (ME), was necessary. These, however, did not prove to be flexible enough for modern applications with varying requirements in terms of the functionality provided by the JDK. In that regard, the need for a modular system came in as a vital requirement, not only to address the modularization of the Java utilities (overall, there are more than 5,000 Java classes and 1,500 C++ source files with more than 250,000 lines of code for the HotSpot runtime), but also to provide a mechanism for developers to create and manage modular applications using the same module system as that used in the JDK. Java 8 provided an intermediate mechanism to enable applications to use only a subset of the APIs provided by the entire JDK, and that mechanism was named compact profiles. In fact, compact profiles also provided the basis for further work that had to be done in order to break dependencies between the various distinct components of the JDK. This breaking of dependencies was required to enable the implementation of a module system in Java.
The module system itself was developed under the name of Project Jigsaw, on the basis of which several Java enhancement proposals and a target Java Specification Request (JSR 376) were formed. A complete restructuring of the JDK code base was made, along with a complete reorganization of the JDK distributable images.
There was considerable controversy in the community as to whether an existing and mature Java module system, such as OSGi, should be adopted as part of the JDK, instead of providing a completely new module system. However, OSGi targets runtime behavior, such as the resolution of module dependencies, installation, uninstallation, starting and stopping of modules (also named bundles in terms of OSGi), custom module classloaders, and so on.
Project Jigsaw, however, targets a compile-time module system where the resolution of dependencies happens when the application is compiled. Moreover, installing and uninstalling a module as part of the JDK eliminates the need to include it explicitly as a dependency during compilation. Furthermore, the loading of module classes is made possible through the existing hierarchy of classloaders (the bootstrap, and the extension and system classloaders).
Additional benefits from the Java module system include enhanced security and performance. By modularizing the JDK and applications into Jigsaw modules, developers are able to create well-defined boundaries between components and their corresponding domains. This separation of concerns aligns with the security architecture of the platform and is an enabler of better resource utilization.
For a long time, there has been no standard shell shipped with the Java programming language to experiment with new language features or libraries, or for rapid prototyping. If you wanted to do this, you could write a test application with a main() method, compile it with javac, and run it. This could be done either in the command line or using a Java IDE; however, in both cases, this is not as convenient as having an interactive shell for the purpose.
Starting an interactive shell in JDK 9 is as simple as running the following command (assuming the bin directory of your JDK 9 installation is in the current path):
You may find it somewhat puzzling that an interactive shell has not been introduced earlier in the Java platform, as many programming languages, such as Python, Ruby, and a number of others, already come with an interactive shell in their earliest versions. However, this didn't make it on the priority features list until Java 9. The Java Shell makes use of a JShell API that provides capabilities to enable autocompletion or evaluation of expressions and code snippets, among other features. Chapter 6, Experimenting with the Java Shell, is dedicated to discussing the details of the Java Shell so that developers can make the best use of it.
Up to JDK 9, if you wanted to create a Java process and handle process input/output, you had to use one of the following approaches:
- The Runtime.getRuntime.exec() method, which allows us to execute a command in a separate OS process. Using this approach would require you to get a java.lang.Process instance over which to provide certain operations in order to manage the external process.
- The new java.lang.ProcessBuilder class, which has some more enhancements with regard to interacting with the external process. You would also need to create a java.lang.Process instance to represent the external process.
Both approaches were inflexible and also nonportable, as the set of commands executed by the external processes were highly dependent on the operating system. An additional effort had to be exerted in order to make the particular process operations portable across multiple operating systems. Chapter 9, Making Use of the Process API, is dedicated to the new process API, providing developers with the knowledge to create and manage external processes in a much easier way.
The G1 garbage collector was already introduced in JDK 7 and is now enabled by default in JDK 9. It is targeted for systems with multiple processing cores and a lot of available memory. What are the benefits of the G1 compared to previous types of garbage collectors? How does it achieve these improvements? Is there a need to tune it manually, and in what scenarios? These and several more questions regarding G1 will be discussed in Chapter 7, Leveraging the Default G1 Garbage Collector.
On many occasions, Java applications may suffer from performance degradation. Exacerbating the issue is a lack of performance tests that can provide at least a minimal set of guarantees that performance requirements are met and, moreover, the performance of certain features will not degrade over time. Measuring the performance of Java applications is not trivial, especially due to the fact that there are a number of compiler and runtime optimizations that may affect performance statistics. For that reason, additional measures, such as warm-up phases and other tricks, must be used in order to provide more accurate performance measurements. The JMH is a framework that incorporates a number of techniques, along with a convenient API that can be used for this purpose. It is not a new tool but was included with the distribution of Java 9. If you have not added JMH to your toolbox yet, read Chapter 8, Microbenchmarking Applications with JMH, to learn about the use of JMH in the context of Java application development.
HTTP 2.0 is the successor to the HTTP 1.1 protocol, and this new version of the protocol addresses some limitations and drawbacks of the previous one. HTTP 2.0 improves performance in several ways and provides capabilities such as request/response multiplexing in a single TCP connection, sending of responses in a server push, flow control, and request prioritization, among others. Java provides the java.net.HttpURLConnection utility that can be used to establish a nonsecure HTTP 1.1 connection. However, the API was considered difficult to maintain, an issue which was further complicated by the need to support HTTP 2.0 and, so, an entirely new client API was introduced in order to establish a connection via the HTTP 2.0 or the web socket protocols. The HTTP 2.0 client, along with the capabilities it provides, will be covered in Chapter 11, New Tools and Tool Enhancements.
Reactive programming is a paradigm used to describe a certain pattern for the propagation of changes in a system. Reactiveness is not built in Java itself, but reactive data flows can be established using third-party libraries, such as RxJava or Project Reactor (part of the Spring Framework). JDK 9 also addressed the need for an API that aids the development of highly responsive applications built around the idea of reactive streams by providing the java.util.concurrent.Flow class for the purpose. The Flow class, along with other related changes introduced in JDK 9, will be covered in Chapter 12, Concurrency Enhancements.
Java 10 was released in March 2018 and had the 11 features listed here, in addition to the previously covered time-based versioning:
- Local variable type inference
- Consolidation of the JDK forest into a single repository
- Garbage collection interface
- Parallel full garbage collector for G1
- Application class-data sharing
- Thread-local handshakes
- Removal of the native-header generation tool (javah)
- Additional Unicode language-tag extensions
- Heap allocation on alternative memory devices
- Experimental Java-based JIT compiler
- Root certificates
A brief overview of these features is covered in this chapter, with more detailed coverage in subsequent chapters.
Starting with Java 10, declaring local variables has been simplified. Developers no longer have to include manifest declarations of local variable types. This is accomplished using the new var identifier, as shown in this example:
var myList = new ArrayList<String>();
Using the preceding code, ArrayList<String> is inferred, so we no longer need to use ArrayList<String> myList = new ArrayList<String>();.
Local variable type inference is covered in Chapter 3, Java 11 Fundamentals.
Prior to Java 10, there were eight repositories for the JDK (CORBA, HotSpot, JDK, JAXP, JAX-WS, langtools, Nashorn, and ROOT). With Java 10, these repositories have been consolidated into a single code base. Notably, Java FX was not part of this consolidation. This topic will be explained further in Chapter 2, Discovering Java 11.
Java 10 ushered in enhancements to the garbage collection process. A new garbage collector interface results in improvements that will be detailed in Chapter 7, Leveraging the Default G1 Garbage Collector.
In Java 10, the G1 full garbage collector was made parallel. Starting with Java 9, G1 was made the default garbage collector, so this change was of special significance. This change will be detailed in Chapter 7, Leveraging the Default G1 Garbage Collector.
Class-data sharing (CDS) has been extended to support faster application startup and smaller footprints. Using CDS, developers can have specific class files pre-parsed and stored in a shareable archive. We will explore this change to the Java platform in Chapter 2, Discovering Java 11.
With Java 10 and beyond, it is possible to stop individual threads without having to perform a global virtual machine safepoint. We will fully explore this change in Chapter 3, Java 11 Fundamentals.
A concerted effort was undertaken to remove the javah tool from the JDK. This change was warranted because of the functionality available in javac. We will detail this change in Chapter 11, New Tools and Tool Enhancements.
The Java platform has supported language tags since Java 7. In Java 10, changes were made to java.util.Local and related APIs to incorporate additional Unicode language tags. Details will be covered in Chapter 2, Discovering Java 11.
The HotSpot virtual machine, as of Java 10, supports non-DRAM memory devices. This will be explained in Chapter 3, Java 11 Fundamentals.
Java 9 introduced us to a Java-based just-in-time (JIT) compiler. This JIT compiler has been enabled for Linux/x64 platforms. This experimental compiler will be further explored in Chapter 14, Command-Line Flags.
Starting with the release of Java 10, there has been a default set of Certification Authority (CA) certificates as part of the JDK. This change and its benefits will be covered in Chapter 3, Java 11 Fundamentals.
Java 11, was released in September 2018 and had four features, as listed here:
- Dynamic class-file constants
- Epsilon—an arbitrarily low-overhead garbage collector
- Removal of the Java EE and CORBA modules
- Local variable syntax for Lambda parameters
A brief overview of these features is covered in this chapter, with more detailed coverage in subsequent chapters.
In Java 11, the file format for Java class files was extended to support CONSTANT_Dynamic, which delegates creation to a bootstrap method. This change will be fully explored in Chapter 3, Java 11 Fundamentals.
Garbage collection enhancements are seemingly part of every Java platform release. Java 11, includes a passive garbage collector that does not reclaim memory. We will explore this in Chapter 7, Leveraging the Default G1 Garbage Collector.
The Java Enterprise Edition (Java EE) and Common Object Request Broker Architecture (CORBA) modules were depreciated in Java 9 and have been removed from the Java platform as of Java 11. Details are provided in Chapter 3, Java 11 Fundamentals.
As discussed earlier in this chapter, the var identifier was introduced in Java 10. With the latest version, Java 11, var can be used in implicitly typed Lambda expressions. This use of the var identifier is covered in Chapter 3, Java 11 Fundamentals.
In this chapter, we explored the newly implemented, time-based versioning system for the Java platform. We also learned, at a high level, the changes introduced in Java 9, 10, and 11 (referred to as versions 9, 18.3, and 18.9 respectively). Java 9's most significant change was modularity based on Project Jigsaw and included additional changes focusing on the Java shell, controlling external process, garbage collection, JHM, and more. Key features of Java 10 were covered, including local variable type inference, JDK consolidation, garbage collection, application CDS, root certificates, and more. Changes introduced in Java 11 included dynamic class-file constants, garbage collection, local variable type inference for Lambdas, and more.
In the next chapter, we will look at several internal changes introduced in the Java platform, including changes from Java 9, 10, and 11.
- What will the first Java version be in 2019?
- What is a key benefit of the new Java time-based release model?
- What was the most significant change to the Java platform with JDK 9?
- What was removed with Java 11: CORBA, Lambda, or G1?
- Does CDS support faster startup or more efficient garbage collection?
- What is Epsilon?
- Is var a datatype, identifier, reserved word, or keyword?
- Which Java release introduced root certificates to the Java platform?
- Which release included enhancements to garbage collection?
- What is the default garbage collector in Java?
This survey chapter took a broad-brush approach to recent changes to the Java platform. If any of the concepts were unfamiliar to you, consider brushing up on your Java knowledge with one or more of the following resources:
- Java: Object-Oriented Programming Concepts [Integrated Course], available at https://www.packtpub.com/application-development/java-object-oriented-programming-concepts-integrated-course.
- Java 9 High Performance, available at https://www.packtpub.com/application-development/java-9-high-performance.