Wednesday, January 11, 2023

Hidden gems in Java 19, Part 2: The real hidden stuff


Compared to previous Java releases, the scope of changes in Java 19 has decreased significantly by targeting only seven implemented JEPs—most of which are new or improved incubator or preview features. As far as affecting your production code, you can take a little breather. However, Java 19 contains thousands of performance, security, and stability updates that aren’t in a JEP—and they are worthy of being adopted.

Oracle Java Tutorial and Material, Oracle Java Prep, Java Preparation, Java Guides, Java Learning, Java Certification

Even if you don’t use any of the preview or incubator features in the JEPs (read all about them in “Hidden gems in Java 19, Part 1: The not-so-hidden JEPs”), you should consider moving to Java 19.

This article outlines the updates buried deep in the release notes, and you and your team should be aware of them. For example, some new features include support for Unicode 14.0, additional date-time formats, new Transport Layer Security (TLS) signature schemes, and defense against Return Oriented Programming (ROP) attacks via PAC-RET protection on AArch64 systems.

I am using the Java 19.0.1 jshell tool to demonstrate the code in this article. If you want to test the features, download JDK 19, fire up your terminal, check your version, and run jshell, as follows. Note that you might see a newer dot-release version of the JDK, but nothing else should change.

[mtaman]:~ java -version
 java version "19.0.1" 2022-10-18
 Java(TM) SE Runtime Environment (build 19.0.1+10-21)
 Java HotSpot(TM) 64-Bit Server VM (build 19.0.1+10-21, mixed mode, sharing)

[mtaman]:~ jshell --enable-preview
|  Welcome to JShell -- Version 19.0.1
|  For an introduction type: /help intro

jshell>

The enhancements


This section describes some additions and enhancements in Java 19.

Support for Unicode 14.0. Java 19 provides a small but significant addition for internationalization; it provides upgrades to Unicode 14.0. The java.lang.Character class now supports Level 14 of the Unicode Character Database (UCD), which adds 838 new characters, 5 new scripts, and 37 new emoji characters.

New system properties for System.out and System.err. If you run an existing application with Java 19, you may see question marks on the console instead of special characters. This is because, as of Java 19, the operating system’s default encoding is used for printing to System.out and System.err.

For example, cp1252 encoding is the default on Windows. If that’s not what you want and you’d prefer to see output in UTF-8, add the following JVM options when calling the application:

-Dstdout.encoding=utf8 -Dstderr.encoding=utf8

Your platform determines what these system properties’ default settings are. When the platform doesn’t have console streams, the values default to the native.encoding property’s value. When necessary, the launcher’s command-line option -D can override the properties and set them to UTF-8.

If you don’t want to do this each time the software launches, you can also define the following environment variable (it starts with an underscore) to set these parameters globally:

_JAVA_OPTIONS="-Dstdout.encoding=utf8 -Dstderr.encoding=utf8"

New methods to create preallocated hash maps and hashsets. You might wonder why you would need new methods to create preallocated hash maps and hashsets. Here’s an example to clarify that more: If you want to create an ArrayList of 180 elements, you could write the following code:

List<String> list = new ArrayList<>(180);

The underlying array, the ArrayList, is allocated directly for 180 elements and does not have to be enlarged several times as you insert the 180 elements.

Similarly, you might try to create a HashMap with 180 preallocated mappings as follows:

Map<String, Integer> map = new HashMap<>(180);

Intuitively, you would think that this new HashMap offers space for 180 mappings. However, it does not! This happens because the HashMap has a default load factor of 0.75 when it is initialized. This indicates that the HashMap gets rebuilt (also called rehashed) with double the size as soon as it is 75% filled. Thus, the new HashMap is initialized with a capacity of 180 and can hold only 135 (180 × 0.75) mappings without being rehashed.

Therefore, to create a HashMap for 180 mappings, calculate the capacity by dividing the number of mappings by the load factor: 180 ÷ 0.75 = 240. So, a HashMap for 180 mappings would be created as follows:

// for 180 mappings: 180 / 0.75 = 240
Map<String, Integer> map = new HashMap<>(240);

Java 19 makes it easier to create a HashMap that has the required mappings without fiddling with load factors by using the new static factory method newHashMap(int).

Map<String, Integer> map = HashMap.newHashMap(180);

Look at the source code to see how it works.

public static <K, V> HashMap<K, V> newHashMap(int numMappings) {
    return new HashMap<>(calculateHashMapCapacity(numMappings));
}

static final float DEFAULT_LOAD_FACTOR = 0.75f;

static int calculateHashMapCapacity(int numMappings) {
    return (int) Math.ceil(numMappings / (double) DEFAULT_LOAD_FACTOR);
}

Similar labor-saving static factory methods have been created in Java 19. Here’s the complete set.

◉ HashMap.newHashMap
◉ LinkedHashMap.newLinkedHashMap
◉ WeakHashMap.newWeakHashMap
◉ HashSet.newHashSet
◉ LinkedHashSet.newLinkedHashSet

TLS signature schemes. Applications now can alter the signature schemes used in specific TLS or Datagram Transport Layer Security (DTLS) connections using two new Java SE methods, setSignatureSchemes() and getSignatureSchemes(), which are found in the class javax.net.ssl.SSLParameters.

The underlying provider may set the default signature schemes for each TLS or DTLS connection. Applications can also alter the provider-specific default signature schemes by using the jdk.tls.server.SignatureSchemes and jdk.tls.client.SignatureSchemes system attributes. The setSignatureSchemes() method overrides the default signature schemes for the specified TLS or DTLS connections if the signature schemes parameter is not null.

It is recommended that when third-party vendors add support for Java 19 or later releases, they also add support for these methods. The JDK SunJSSE provider supports this technique. However, you should be aware that a provider might not have received an update to support the new APIs, in which case the provider might disregard the established signature schemes.

Support for PAC-RET protection on Linux/AArch64. To defend against Return Oriented Programming (ROP) attacks (documentation here), OpenJDK uses hardware features from the ARM v8.3 Pointer Authentication Code (PAC) extension but only when they are enabled.

To use this functionality, OpenJDK must first be compiled using GCC 9.1.0+ or LLVM 10+ with the configuration flag --enable-branch-protection. Then, if the system supports it and the Java binary was compiled with branch protection enabled, the runtime flag -XX:UseBranchProtection=standard will enable PAC-RET protection; otherwise, the flag is quietly ignored. A warning will be printed to the console if the system does not support PAC-RET protection or if the Java binary was not built with branch protection enabled. As an alternative, -XX:UseBranchProtection=pac-ret also enables PAC-RET protection.

Additional date-time formats. Java 19 brings new formats to the java.time.format.DateTimeFormatter and DateTimeFormatterBuilder classes. In prior releases, only four predefined styles were available: FormatStyle.FULL, FormatStyle.LONG, FormatStyle.MEDIUM, and FormatStyle.SHORT. Now you can specify a flexible style with the new DateTimeFormatter.ofLocalizedPattern(String requestedTemplate) method.

For example, the following creates a formatter that may format a date according to a locale, for example, “Feb 2022” in the US locale and “2022年2月” in the Japanese locale:

DateTimeFormatter.ofLocalizedPattern("yMMM")

There’s also a new supporting function: DateTimeFormatterBuilder.appendLocalized(String requestedTemplate).

Automatic generation of the class data sharing archive. With Java 19, the JVM option -XX:+AutoCreateSharedArchive automatically creates or updates an application’s class data sharing (CDS) archive, for example

java -XX:+AutoCreateSharedArchive -XX:SharedArchiveFile=app.jsa -cp application.jar App

The specified CDS archive will be written if it does not exist or if a different version of the JDK generated it.

Javadoc search enhancements. Java 19 can create a standalone search page for the API documentation produced by Javadoc, and the search syntax has been improved to support multiple search terms.

Highlighting of deprecated elements, variables, and keywords. The Java Shell tool (jshell) now marks deprecated elements and highlights deprecated variables and keywords in the console.

Specified stack size no longer rounded up. Historically, the actual Java thread stack size might differ from the value provided by the -Xss command-line option; it might be rounded up to a multiple of the system page size when that’s required by the operating system. That’s been fixed in Java 19, so the stack size specified is what you get.

Larger default key sizes for cryptographic algorithms. What happens if the caller does not specify a key size when using a KeyPairGenerator or KeyGenerator object to generate a key pair or secret key? In such cases, JDK providers use provider-specific default values.

Java 19 increases the default key sizes for various cryptographic algorithms as follows:

◉ Elliptic Curve Cryptography (ECC): increased from 256 to 384 bits
◉ Rivest-Shamir-Adleman (RSA), RSASSA-PSS, and Diffie-Hellman (DH): increased from 2,048 to 3,072 bits
◉ Advanced Encryption Standard (AES): increased from 128 to 256 bits, if permitted by the cryptographic policy; otherwise, it falls back to 128

In addition, the default digest method used by the jarsigner tool has changed from SHA-256 to SHA-384. The jarsigner tool’s default signature algorithm has also been modified to reflect this. Except for more-extended key sizes whose security strength matches SHA-512, SHA-384 is used instead of SHA-256.

Note that jarsigner will keep using SHA256withDSA as the default signature algorithm for Digital Signature Algorithm (DSA) keys to help with interoperability with earlier Java editions.

Linux cpu.shares argument no longer misinterpreted. The Linux cgroups argument cpu.shares was improperly interpreted by earlier JDK editions. When the JVM was run inside a container, this could result in the JVM using fewer CPUs than were available, underutilizing CPU resources.

With Java 19, the JVM will no longer, by default, take cpu.shares into account when determining how many threads to allocate to the various thread pools.

To return to the old behavior, use the command-line option -XX:+UseContainerCpuShares, but be aware that this option is deprecated and might be eliminated in a subsequent JDK release.

Upgraded support for locale data. Locale data based on the Unicode Common Locale Data Repository (CLDR) has been upgraded to version 41. Refer to the Unicode Consortium’s CLDR release notes for the list of changes.

Bug fixes and changes


This section describes some of the bug fixes and changes in Java 19.

Source- and binary-incompatible changes to java.lang.Thread. With the preview introduction of virtual threads in Java 19’s JEP 425, some source and binary changes have been made to the class java.lang.Thread that may impact your code if you extend the class. More details are in the documentation. Be aware of the following changes:

◉ Three new final methods have been added: Thread.isVirtual(), Thread.threadId(), and Thread.join(Duration). Suppose there is existing compiled code that extends Thread, and the subclass declares a method with the same name, parameters, and return type as any of these methods. In such a case, IncompatibleClassChangeError will be thrown at runtime if the subclass is loaded.
◉ The Thread class defines several new methods. If one of your source code files extends Thread and a method in the subclass conflicts with any of the new Thread methods, the file will not compile without being changed.
◉ Thread.Builder is added as a nested interface. If one of your source code files extends Thread and imports a class named Builder, and the code in the subclass references Builder as a simple name, the file will not compile without being changed.

Indify string concatenation changes to the order of operations. In Java 19, the process of concatenating strings now evaluates each parameter and eagerly creates a string from left to right. This fixes a bug in Java 9’s JEP 280, which introduced string concatenation techniques based on invokedynamic. (By the way, the word indify is short for using invokedynamic.)

For example, the following code now prints zoozoobar not zoobarzoobar:

StringBuilder builder = new StringBuilder("zoo");
System.out.println("" + builder + builder.append("bar"));

Lambda deserialization for object method references on interfaces. Deserialization of serialized method references to Object methods, which used an interface as the type on which the method is invoked, can now be deserialized again.

Keep in mind that the class files must be recompiled to support deserialization.

POSIX file access attributes copied to the target on a foreign file system. When two files are linked to different file system providers, such as when you copy a file from the default file system to a zip file system, the Java 19 function java.nio.file.Files.copy(Path, Path) copies Portable Operating System Interface (POSIX) file attributes from the source file to the destination file.

The POSIX file attribute view must be supported by both the source and target file systems. The owner and group owner of the file are not copied; the POSIX attributes copied are restricted to the file access rights.

Methods of InputStream and FilterInputStream no longer synchronized. The mark and reset functions of the java.io.InputStream and java.io.FilterInputStream classes no longer use the keyword synchronized. Since the other methods in these classes do not synchronize, this keyword is useless and has been removed in Java 19.

Some returned strings slightly different. In Java 19, the specification of the Double.toString(double) and Float.toString(float) methods is now tighter than in earlier releases, and the new implementation fully adheres to the specification.

The result of this change is that some returned strings are now shorter than when earlier Java releases are used, and inputs at the extremes of the subnormal ranges near zero might look different. However, the number of cases where there’s a difference in output is relatively small compared to the sheer number of possible double and float inputs.

For example, the double subnormal range is Double.toString(1e-323), which now returns 9.9E-324, as mandated by the new specification. Another example: Double.toString(2e23) now returns 2.0E23; in earlier releases, it returns 1.9999999999999998E23.

User’s home directory set to $HOME if invalid. The user.home system property on Linux and macOS systems is set to the operating system’s specified home directory. The value of the environment variable $HOME is used in place of the directory name if the variable is empty or contains only one character.

Typically, $HOME has a valid value and the same directory name. Except in systems such as system on Linux or when running in a container such as Docker, the default to $HOME is unusual and unlikely to happen.

Java 19 was changed to use the correct user home directory.

Deprecation


This section describes the features, options, and APIs deprecated in Java 19.

Deprecation of Locale class constructors. In Java 19, the public constructors of the Locale class were marked as deprecated. You should use the new static factory method Locale.of() to ensure only one instance per Locale configuration.

The following example shows the use of the factory method compared to the old constructor:

Locale japanese = new Locale("ja"); // deprecated
Locale japan    = new Locale("ja", "JP"); // deprecated

Locale japanese1 = Locale.of("ja");
Locale japan1    = Locale.of("ja", "JP");

System.out.println("japanese  == Locale.JAPANESE = " + (japanese  == Locale.JAPANESE));
System.out.println("japan     == Locale.JAPAN    = " + (japan     == Locale.JAPAN));
System.out.println("japanese1 == Locale.JAPANESE = " + (japanese1 == Locale.JAPANESE));
System.out.println("japan1    == Locale.JAPAN    = " + (japan1    == Locale.JAPAN));

When you run this code, you will see that the objects supplied via the factory method are identical to the Locale constants, whereas those created with constructs logically are not.

Several java.lang.ThreadGroup methods degraded. In Java 14 and Java 16, many Thread and ThreadGroup methods were marked as deprecated for removal. Now, the following methods have been decommissioned in Java 19:

◉ ThreadGroup.destroy() invocations will be ignored.
◉ ThreadGroup.isDestroyed() always returns false.
◉ ThreadGroup.setDaemon() sets the daemon flag, but this has no effect.
◉ ThreadGroup.getDaemon() returns the value of the unused daemon flags.
◉ ThreadGroup.suspend(), resume(), and stop() throw an UnsupportedOperationException.

Removed items


This section describes the old features, options, and APIs removed in Java 19.

TLS cipher suites using 3DES removed from the default enabled list. The default list of allowed cipher suites no longer includes the following TLS cipher suites that employ the outdated Triple Data Encryption (3DES) algorithm:

◉ TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA
◉ TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA
◉ TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA
◉ TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA
◉ SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA
◉ SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA
◉ SSL_RSA_WITH_3DES_EDE_CBC_SHA

Note that cipher suites using 3DES are already disabled by default in the jdk.tls.disabledAlgorithms security property. To turn them back on, you can remove 3DES_EDE_CBC from the jdk.tls.disabledAlgorithms security parameter and re-enable the suites using the setEnabledCipherSuites() function of the SSLSocket, SSLServerSocket, or SSLEngine classes. While you are free to use these suites, you do so at your own risk; these were removed for a reason!

Alternately, the https.cipherSuites system property can be used to re-enable the suites if an application is using the HttpsURLConnection class.

Removal of the GCParallelVerificationEnabled diagnostic flag. Disabling parallel heap verification has never been used other than with its default value because there are no known benefits to doing so. Additionally, for a very long time, with no problems, this default value permitted multithreaded verification. Therefore, the GCParallelVerificationEnabled diagnostic flag was removed.

SSLSocketImpl finalizer implementation removed. Because the Socket implementation now handles the underlying native resource releases, the finalizer implementation of SSLSocket has been abandoned. With Java 19, if SSLSocket is not explicitly closed, TLS close_notify messages won’t be sent.

If you fail to correctly close sockets, you might see a runtime error. Applications should never rely on garbage collection and should always permanently close sockets.

Alternate ThreadLocal implementation of the Subject::current and Subject::callAs APIs removed. The jdk.security.auth.subject.useTL system property and the alternate ThreadLocal implementation of the Subject::current and Subject::callAs APIs have been removed. The default implementation of these APIs is still supported.

Source: oracle.com

Related Posts

0 comments:

Post a Comment