Showing posts with label Java 9. Show all posts
Showing posts with label Java 9. Show all posts

Thursday, January 31, 2019

JDK 9/JEP 280: String Concatenations Will Never Be the Same

JEP 280 ("Indify String Concatenation") was implemented in conjunction with JDK 9 and, according to its "Summary" section, "Change[s] the static String-concatenation bytecode sequence generated by javac to use invokedynamic calls to JDK library functions." The impact this has on string concatenation in Java is most easily seen by looking at the javap output of classes using string concatenation that are compiled in pre-JDK 9 and post-JDK 9 JDKs.

The following simple Java class named "HelloWorldStringConcat" will be used for the first demonstration.

Contrasting of the differences in -verbose output from javap for the HelloWorldStringConcat class's main(String) method when compiled with JDK 8 (AdoptOpenJDK) and JDK 11 (Oracle OpenJDK) is shown next. I have highlighted some key differences.

JDK 8 javap Output

Classfile /C:/java/examples/helloWorld/classes/dustin/examples/HelloWorldStringConcat.class
  Last modified Jan 28, 2019; size 625 bytes
  MD5 checksum 3e270bafc795b47dbc2d42a41c8956af
  Compiled from "HelloWorldStringConcat.java"
public class dustin.examples.HelloWorldStringConcat
  minor version: 0
  major version: 52
     . . .
  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=4, locals=1, args_size=1
         0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
         3: new           #3                  // class java/lang/StringBuilder
         6: dup
         7: invokespecial #4                  // Method java/lang/StringBuilder."":()V
        10: ldc           #5                  // String Hello,
        12: invokevirtual #6                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        15: aload_0
        16: iconst_0
        17: aaload
        18: invokevirtual #6                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        21: invokevirtual #7                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
        24: invokevirtual #8                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        27: return

JDK 11 javap Output

Classfile /C:/java/examples/helloWorld/classes/dustin/examples/HelloWorldStringConcat.class
  Last modified Jan 28, 2019; size 908 bytes
  MD5 checksum 0e20fe09f6967ba96124abca10d3e36d
  Compiled from "HelloWorldStringConcat.java"
public class dustin.examples.HelloWorldStringConcat
  minor version: 0
  major version: 55
     . . .
  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
    Code:
      stack=3, locals=1, args_size=1
         0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
         3: aload_0
         4: iconst_0
         5: aaload
         6: invokedynamic #3,  0              // InvokeDynamic #0:makeConcatWithConstants:(Ljava/lang/String;)Ljava/lang/String;
        11: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        14: return

The "Description" section of JEP 280 describes this difference: "The idea is to replace the entire StringBuilder append dance with a simple invokedynamic call to java.lang.invoke.StringConcatFactory, that will accept the values in the need of concatenation." This same section shows a similar comparison of compiled output for a similar string concatenation example.

The compiled output from JDK 11 for the simple string concatenation is not just fewer lines than its JDK 8 counterpart; it also has fewer "expensive" operations. Potential performance improvement can be gained from not needing to wrap primitive types, and not needing to instantiate a bunch of extra objects. One of the primary motivations for this change was to "lay the groundwork for building optimized String concatenation handlers, implementable without the need to change the Java-to-bytecode compiler" and to "enable future optimizations of String concatenation without requiring further changes to the bytecode emitted by javac."

JEP 280 Does Not Affect StringBuilder or StringBuffer

There's an interesting implication of this in terms of using StringBuffer (which I have a difficult time finding a good use for anyway) and StringBuilder. It was a stated "Non-Goal" of JEP 280 to not "introduce any new String and/or StringBuilder APIs that might help to build better translation strategies." Related to this, for simple string concatenations like that shown in the code example at the beginning of this post, explicit use of StringBuilder and StringBuffer will actually preclude the ability for the compiler to make use of the JEP 280-introduced feature discussed in this post.

The next two code listings show similar implementations to the simple application shown above, but these use StringBuilder and StringBuffer respectively instead of string concatenation. When javap -verbose is executed against these classes after they are compiled with JDK 8 and with JDK 11, there are no significant differences in the main(String[]) methods.

Explicit StringBuilder Use in JDK 8 and JDK 11 Are The Same

JDK 8 javap Output for HelloWorldStringBuilder.main(String[])

Classfile /C:/java/examples/helloWorld/classes/dustin/examples/HelloWorldStringBuilder.class
  Last modified Jan 28, 2019; size 627 bytes
  MD5 checksum e7acc3bf0ff5220ba5142aed7a34070f
  Compiled from "HelloWorldStringBuilder.java"
public class dustin.examples.HelloWorldStringBuilder
  minor version: 0
  major version: 52
     . . .
  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=4, locals=1, args_size=1
         0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
         3: new           #3                  // class java/lang/StringBuilder
         6: dup
         7: invokespecial #4                  // Method java/lang/StringBuilder."":()V
        10: ldc           #5                  // String Hello,
        12: invokevirtual #6                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        15: aload_0
        16: iconst_0
        17: aaload
        18: invokevirtual #6                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        21: invokevirtual #7                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
        24: invokevirtual #8                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        27: return

JDK 11 javap Output for HelloWorldStringBuilder.main(String[])

Classfile /C:/java/examples/helloWorld/classes/dustin/examples/HelloWorldStringBuilder.class
  Last modified Jan 28, 2019; size 627 bytes
  MD5 checksum d04ee3735ce98eb6237885fac86620b4
  Compiled from "HelloWorldStringBuilder.java"
public class dustin.examples.HelloWorldStringBuilder
  minor version: 0
  major version: 55
     . . .
  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
    Code:
      stack=4, locals=1, args_size=1
         0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
         3: new           #3                  // class java/lang/StringBuilder
         6: dup
         7: invokespecial #4                  // Method java/lang/StringBuilder."":()V
        10: ldc           #5                  // String Hello,
        12: invokevirtual #6                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        15: aload_0
        16: iconst_0
        17: aaload
        18: invokevirtual #6                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        21: invokevirtual #7                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
        24: invokevirtual #8                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        27: return

Explicit StringBuffer Use in JDK 8 and JDK 11 Are The Same

JDK 8 javap Output for HelloWorldStringBuffer.main(String[])

Classfile /C:/java/examples/helloWorld/classes/dustin/examples/HelloWorldStringBuffer.class
  Last modified Jan 28, 2019; size 623 bytes
  MD5 checksum fdfb90497db6a3494289f2866b9a3a8b
  Compiled from "HelloWorldStringBuffer.java"
public class dustin.examples.HelloWorldStringBuffer
  minor version: 0
  major version: 52
     . . .
  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=4, locals=1, args_size=1
         0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
         3: new           #3                  // class java/lang/StringBuffer
         6: dup
         7: invokespecial #4                  // Method java/lang/StringBuffer."":()V
        10: ldc           #5                  // String Hello,
        12: invokevirtual #6                  // Method java/lang/StringBuffer.append:(Ljava/lang/String;)Ljava/lang/StringBuffer;
        15: aload_0
        16: iconst_0
        17: aaload
        18: invokevirtual #6                  // Method java/lang/StringBuffer.append:(Ljava/lang/String;)Ljava/lang/StringBuffer;
        21: invokevirtual #7                  // Method java/lang/StringBuffer.toString:()Ljava/lang/String;
        24: invokevirtual #8                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        27: return

JDK 11 javap Output for HelloWorldStringBuffer.main(String[])

Classfile /C:/java/examples/helloWorld/classes/dustin/examples/HelloWorldStringBuffer.class
  Last modified Jan 28, 2019; size 623 bytes
  MD5 checksum e4a83b6bb799fd5478a65bc43e9af437
  Compiled from "HelloWorldStringBuffer.java"
public class dustin.examples.HelloWorldStringBuffer
  minor version: 0
  major version: 55
     . . .
  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
    Code:
      stack=4, locals=1, args_size=1
         0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
         3: new           #3                  // class java/lang/StringBuffer
         6: dup
         7: invokespecial #4                  // Method java/lang/StringBuffer."":()V
        10: ldc           #5                  // String Hello,
        12: invokevirtual #6                  // Method java/lang/StringBuffer.append:(Ljava/lang/String;)Ljava/lang/StringBuffer;
        15: aload_0
        16: iconst_0
        17: aaload
        18: invokevirtual #6                  // Method java/lang/StringBuffer.append:(Ljava/lang/String;)Ljava/lang/StringBuffer;
        21: invokevirtual #7                  // Method java/lang/StringBuffer.toString:()Ljava/lang/String;
        24: invokevirtual #8                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        27: return

JDK 8 and JDK 11 Handling of Looped String Concatenation

For my last example of JEP 280 changes in action, I use a code sample that may offend some Java developers' sensibilities and perform a string concatenation within a loop. Keep in mind this is just an illustrative example and all will be okay, but don't try this at home.

JDK 8 javap Output for HelloWorldStringConcatComplex.main(String[])

Classfile /C:/java/examples/helloWorld/classes/dustin/examples/HelloWorldStringConcatComplex.class
  Last modified Jan 30, 2019; size 766 bytes
  MD5 checksum 772c4a283c812d49451b5b756aef55f1
  Compiled from "HelloWorldStringConcatComplex.java"
public class dustin.examples.HelloWorldStringConcatComplex
  minor version: 0
  major version: 52
     . . .
  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=3, args_size=1
         0: ldc           #2                  // String Hello
         2: astore_1
         3: iconst_0
         4: istore_2
         5: iload_2
         6: bipush        25
         8: if_icmpge     36
        11: new           #3                  // class java/lang/StringBuilder
        14: dup
        15: invokespecial #4                  // Method java/lang/StringBuilder."":()V
        18: aload_1
        19: invokevirtual #5                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        22: iload_2
        23: invokevirtual #6                  // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
        26: invokevirtual #7                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
        29: astore_1
        30: iinc          2, 1
        33: goto          5
        36: getstatic     #8                  // Field java/lang/System.out:Ljava/io/PrintStream;
        39: aload_1
        40: invokevirtual #9                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        43: return

JDK 11 javap Output for HelloWorldStringConcatComplex.main(String[])

Classfile /C:/java/examples/helloWorld/classes/dustin/examples/HelloWorldStringConcatComplex.class
  Last modified Jan 30, 2019; size 1018 bytes
  MD5 checksum 967fef3e7625965ef060a831edb2a874
  Compiled from "HelloWorldStringConcatComplex.java"
public class dustin.examples.HelloWorldStringConcatComplex
  minor version: 0
  major version: 55
     . . .
  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=3, args_size=1
         0: ldc           #2                  // String Hello
         2: astore_1
         3: iconst_0
         4: istore_2
         5: iload_2
         6: bipush        25
         8: if_icmpge     25
        11: aload_1
        12: iload_2
        13: invokedynamic #3,  0              // InvokeDynamic #0:makeConcatWithConstants:(Ljava/lang/String;I)Ljava/lang/String;
        18: astore_1
        19: iinc          2, 1
        22: goto          5
        25: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
        28: aload_1
        29: invokevirtual #5                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        32: return

In the presentation "Enough java.lang.String to Hang Ourselves ...," Dr. Heinz M. Kabutz and Dmitry Vyazelenko discuss the JEP 280-introduced changes to Java string concatenation and summarize it succinctly, "+ is no longer compiled to StringBuilder." In their "Lessons from Today" slide, they state, "Use + instead of StringBuilder where possible" and "recompile classes for Java 9+."

The changes implemented in JDK 9 for JEP 280 "will enable future optimizations of String concatenation without requiring further changes to the bytecode emitted by javac." Interestingly, it was recently announced that JEP 348 ("Java Compiler Intrinsics for JDK APIs") is now a Candidate JEP and it aims to use a similar approach for compiling methods String::format and Objects::hash.

Tuesday, January 8, 2019

Explicitly Naming Automatic Java Modules

Nicolas Fränkel recently published the surprising post "A hard look at the state of Java modularization." In that post, Fränkel provides the results of his investigation into support available in the 29 libraries referenced in the blog post "20 popular Java libraries" for modules introduced with JDK 9. Fränkel's investigation aimed to identify which of these popular Java libraries was "modularized" (fully implemented module defined in module-info.java) or provided at least an "automatic module name" via MANIFEST.MF even if the library isn't modularized.

Of the 29 popular Java libraries investigated, Fränkel identified only two (SLF4J 1.8.0-beta2 and JAXB 2.3.1) that are fully modularized. Of the remaining 27 libraries that are not fully modularized, 12 do have an automatic module name defined. That means, of course, that 15 of the 29 libraries (just over 50%) do not have any explicit support for modularity introduced with JDK 9!

In the post "Automatic-Module-Name: Calling all Java Library Maintainers" a little over one year ago, Sander Mak (one of the authors of Java 9 Modularity) describes "what needs to be done to move the Java library ecosystem toward modules." Mak explains that "support for the Java module system can be incrementally added to libraries" and uses this post to "explain the first step ... to becoming a Java module." Mak writes:

This first step boils down to picking a module name, and adding it as Automatic-Module-Name: <module name> entry to the library's MANIFEST.MF. That's it. With this first step you make your library usable as Java module without moving the library itself to Java 9 or creating a module descriptor for the library, yet.

In the "Automatic-Module-Name: Calling all Java Library Maintainers" post, Mak also provides guidance for providing an automatic name for a module. He recommends picking an explicit name for the module rather than relying on the ModuleFinder-based name derivation algorithm (module named based on JAR filename). Mak references Stephen Colebourne's post "Java SE 9 - JPMS automatic modules," in which Colebourne concludes, "Community members must at all costs avoid publishing modular jar files that depend on filenames." Incidentally, Colebourne's post "Java SE 9 - JPMS module naming" provides additional guidance on module naming.

The naming of the automatic module is significant because later changes to that name will cause backwards incompatibilities for the library. It is also important to not have the module name collide with others' libraries' module names. The recommended way of doing this is to use the root package name contained within the module, assuming that package uses the typical Java package naming convention to ensure uniqueness.

Mak also outlines in his post some "potential issues you need to verify" before adding the Automatic-Module-Name entry to the MANIFEST.MF file to avoid "false expectations." See the "Sanity-Check Your Library" section of Mak's post for the full list and detailed description of these issues which include not using internal JDK types and not having any classes in the unnamed package.

Before concluding my post, I am going to briefly present the difference between an explicitly named automatic module and an implicitly named automatic module. For this, I'll be using a JAR file generated from the code example in my previous post "Parsing Value from StreamCorruptedException: invalid stream header Message." The Java source is not that important for my purposes here other than to point out that the main class used in my post has the package name dustin.utilities.io.

The generated JAR file, which I've called io-examples.jar is not modularized (does not have a module-info file). The jar tool's option --describe-module can be used to quickly determine the automatic module name of the JAR file.

The next two screen snapshots show the results of running jar with the --describe-module option against the JAR. The first screen snapshot indicates the results when the JAR has nothing in its MANIFEST.MF file to indicate an automatic module name. The result is a automatic module named after the JAR name. The second screen snapshot shows the results from running jar --describe-module against the almost identical JAR except for the addition of the attribute Automatic-Module-Name: dustin.utilities.io to the JAR's MANIFEST.MF file. In that case, the automatic module is named the explicitly provided name provided in the manifest file (dustin.utilities.io).

Fränkel opens his post with the assertion, "With the coming of Java 11, the latest Long-Term Support, I think it's a good time to take a snapshot of the state of modularization." I too think that recent and pending changes make it more important for all of us in the Java community to start understanding Java's built-in modularity and its implications and to take steps toward more complete modularity support.

Additional (Previously Referenced) Resources

Tuesday, June 26, 2018

Deferred Execution with Java's Consumer

In an earlier blog post ("Deferred Execution with Java's Supplier") I referenced Cay Horstmann's statement in the book "Java SE8 for the Really Impatient" regarding lambda expressions, "The point of all lambdas is deferred execution." Horstmann wrote an article called "Lambda Expressions in Java 8" for Dr. Dobb's magazine in its final year in which he wrote a similar statement using different terminology, "A lambda expression is a block of code that you can pass around so it can be executed later, just once or multiple times."

In that earlier post, I looked at how the standard functional interface Supplier is used with lambda expressions in the JDK to support deferred execution for cases where a single value is "supplied only when necessary" and without any argument passed to it. In this post, I focus on JDK-provided examples of using the Consumer standard functional interface to "consume" or "process" a particular code block "only when necessary." Whereas the Supplier accepts no arguments and returns exactly one response, the Consumer accepts one or more arguments and returns no response. The method invoked on a Supplier is the get() method and it is the accept(T) method for a Consumer. By definition, the Consumer is expected to have "side effects" as it "consumes" the provided code block.

There are numerous Consumer-style standard functional interfaces supplied in the java.util.function package. None of these returns a result (that's why they're consumers!), but they differ in the number and types of arguments they accept (but they all accept at least one argument). These are listed here:

  • Consumer - General Consumer that accepts a single argument and will be the center of attention for most of this post's examples.
  • BiConsumer - Accepts two arguments instead of one ("two-arity specialization of Consumer")
  • DoubleConsumer - Specialized Consumer intended for primitive doubles
  • IntConsumer - Specialized consumer for primitive ints
  • LongConsumer - Specialized Consumer intended for primitive longs
  • ObjDoubleConsumer - Specialized Consumer that accepts two arguments, with the first of type Object and the second of type double
  • ObjIntConsumer - Specialized Consumer that accepts two arguments, with the first of type Object and the second of type int
  • ObjLongConsumer - Specialized Consumer that accepts two arguments, with the first of type Object and the second of type long

The remainder of this post will look at a subset of the JDK uses of Consumer and related classes to help demonstrate how and when they are useful.

Peeking at Flow of Stream Elements

In the blog post "Peeking Inside Java Streams with Stream.peek," I discussed the intermediate operation Stream.peek(Consumer) that can be used to view the flowing elements of a stream. This can be very useful for understanding what the various stream operations are doing to their respective stream elements. A common way to do this is to have the Consumer provided to the peek method be a call to System.out.println that prints the currently processed stream element to standard output (or log the element or print it to standard error). An example of this is provided in the Javadoc documentation for the Stream.peek(Consumer) method:

Stream.of("one", "two", "three", "four")
   .filter(e -> e.length() > 3)
   .peek(e -> System.out.println("Filtered value: " + e))
   .map(String::toUpperCase)
   .peek(e -> System.out.println("Mapped value: " + e))
   .collect(Collectors.toList());

Because the various overloaded versions of the println(-) method accept a parameter but do not return anything, they fit perfectly with the "Consumer" concept.

Specifying Action Upon Iterated Stream Elements

While Stream.peek(Consumer) is an intermediate operation, Stream provides two other methods that accept a Consumer that are both terminal operations and are both "for each" methods. The method Stream.forEach​(Consumer) is a method that performs the action specified by the provided Consumer in an "explicitly nondeterministic" manner on the stream's elements. The method Stream.forEachOrdered(Consumer) performs the action specified by the provided Consumer in "the encounter order" of the stream if that stream has an encounter order. In both methods' cases, the Consumer-based "action" should be "non-interfering." Both methods are demonstrated below.

Set.of("one", "two", "three", "four")
   .stream()
   .forEach(i -> out.println(i.toUpperCase()));

Stream.of("one", "two", "three", "four")
   .forEach(i -> out.println(i.toUpperCase()));

List.of("one", "two", "three", "four")
   .stream()
   .forEachOrdered(i -> out.println(i.toUpperCase()));

Stream.of("one", "two", "three", "four")
   .forEachOrdered(i -> out.println(i.toUpperCase()));

The above examples look and very similar. The most obvious situation in which forEach could lead to dramatically different results than forEachOrdered is when parallel stream processing is employed. In that case, it makes most sent to use forEach instead of forEachOrdered.

Specifying Action Upon Iterable Elements

The previous code examples showed using Stream.forEach(Consumer) methods to iterate a stream. The examples also demonstrated doing this against a Set and List by first calling stream() on these collections. There are convenience methods, however, that are defined by Iterable and implemented by these collection implementations which accept a Consumer and allow for iteration of that collection using the forEach method. Examples of this are shown in the next code listing.

Set.of("one", "two", "three", "four")
   .forEach(i -> out.println(i.toUpperCase()));
List.of("one", "two", "three", "four")
   .forEach(i -> out.println(i.toUpperCase()));

Although I used collections in my example above, anything that implements Iterable will generally support the forEach method (or be in violation of the interface's advertised contract).

Specifying Action Upon Iteration of Map Entries

Although Java's Map interface does not extend the Iterable interface like Set and List do, the Java Map was still provided with a similar capability to specify a consumer to "consume" each entry in the Map. Because a Map has two input arguments (key and value), its forEach method accepts a BiConsumer instead of the Consumer discussed so far in this post. A simple example is shown next.

Map.of("Denver", "Colorado",
       "Cheyenne", "Wyoming",
       "Salt Lake City", "Utah",
       "Boise", "Idaho")
   .forEach((c, s) -> out.println(c + " is the capital of " + s));

Walking the Stack

The StackWalker is a welcome addition to JDK 9 that provides a thread-safe approach to perusing a stack trace and is a significant improvement over the StackTraceElement approach. It's arguably more common for developers to use StackWalker.walk(Function), but this post is about Consumer and so the focus is on StackWalker.forEach(Consumer). This method is similar to the previously discussed Stream.forEach and Iterable.forEach methods and is demonstrated in the next code listing.

StackWalker.getInstance().forEach(out::println);

Although there are many more JDK uses of Consumer, of BiConsumer, and of the other types of standard Consumer-style functional interfaces, the last examples I'll cover in this post come from the Optional class.

Applying Only When Present

The methods Optional.ifPresent(Consumer) and Optional.ifPresentOrElse(Consumer) defer the execution of the provided Consumers such that the provided Consumer will only be invoked if the Optional is not "empty" (contains a non-null value). This is a simple but powerful concept and the simplistic and contrived examples show how they work.

public void demonstrateOptionalIfPresent()
{
   getMiddleName(true).ifPresent(n -> out.println("Middle Name: " + n));
}

public void demonstrateOptionalIfPresentOrElse()
{
   getMiddleName(false).ifPresentOrElse(
      n -> out.println("Middle Name: " + n),
      () -> displayMissingMiddleName());
}

private Optional<String> getMiddleName(final boolean present)
{
   return present ? Optional.of("Wayne") : Optional.empty();
}

private void displayMissingMiddleName()
{
   out.println("No middle name provided!");
}

As the above code listing demonstrates, both Optional.ifPresent and JDK 9-introduced Optional.ifPresentOrElse() only invoke the provided Consumer if the Optional is not empty. If the Optional is empty, the ifPresent method does nothing and the ifPresentOrElse invokes the second argument (a Runnable).

The standard Java functional interfaces that accept one or more arguments and return no result include the general Consumer as well as some specialized consumers. These are useful for deferring execution until a given condition occurs (such as being iterated upon or being determined to be present) and the behavior to apply when that condition occurs involves one or more input arguments and no need to provide a response. The source code examples shown in this post are available on GitHub.

Thursday, June 7, 2018

JDK 9/10/11: Side Effects from += on Java String

The question "Why does `array[i++%n] += i+" "` give different results in Java 8 and Java 10?" was posted earlier this week on StackOverflow.com. It points to a bug in the Java compiler that is present in JDK 9 and later, but is not present in JDK 8.

As explained on the StackOverflow thread, Didier L provided a simple example of Java code that reproduces this issue. That is adapted in the code listing shown next.

package dustin.examples.strings;

import static java.lang.System.out;

/**
 * Example demonstrating JDK-8204322 and adapted from Didier L's
 * original example (https://stackoverflow.com/q/50683786).
 */
public class StringConcatenationBug
{
   static void didierLDemonstration()
   {
      final String[] array = {""};
      array[generateArrayIndex()] += "a";
   }

   static int generateArrayIndex()
   {
      out.println("Array Index Evaluated");
      return 0;
   }

   public static void main(final String[] arguments)
   {
      didierLDemonstration();
   }
}

Reading the code shown above, one would expect to see the string "Array Index Evaluated" displayed once if this class's main(String[]) function was executed. With JDK 8, that was the case, but since JDK 9, it has not been the case. The next screen snapshot demonstrates this. The examples shown in the screen snapshot show that when the class is compiled with javac's -source and -target flags set to "8", the string is shown only once when the compiled class is executed. However, when javac's -source and -target flags are set to "9", the string is shown twice when the compiled class is executed.

This bug exists in JDK 9, JDK 10, and JDK 11. Olivier Grégoire has described this bug, "The issue seems to be limited to the string concatenation and assignment operator (+=) with an expression with side effect(s) as the left operand."

JDK-8204322 ["'+=' applied to String operands can provoke side effects"] has been written for this bug, has been resolved, and its resolution is targeted currently for JDK 11. The bug report describes the problem, "When using the += operator, it seems that javac duplicates the code before the +=." It also explains that code written like array[i++%n] += i + " "; is compiled effectively to code like array[i++%n] = array[i++%n] + i + " ";. Jan Lahoda's comment on the bug describes why it occurs. Aleksey Shipilev has requested that this fix be backported to JDK 10 and it appears that it will be via JDK-8204340.

Additional background information regarding this bug can be found in the previously mentioned StackOverflow thread, in the related StackOverflow chat, and on the OpenJDK compiler-dev mailing list threads "Compiler bug about string concatenation" and "RFR: 8204322: '+=' applied to String operands can provoke side effects".

Monday, February 5, 2018

Java 8: Bastion of Long-term Support

Stephen Colebourne's post "Java 9 has six weeks to live" starts, "Java 9 is obsolete in just six weeks." Colebourne references the Mark Reinhold blog post "Moving Java Forward Faster" and writes, "The new Java release train means that there will be a new release of Java every six months. And when the next release comes out, the previous release is obsolete." Colebourne points out that those still on Java 8 can enjoy this "current LTS (long term support) release until the next LTS release occurs (Java 11)." However, for those who have already moved to Java 9, different choices must be made and Colebourne outlines these choices at a high level. Colebourne outlines several types of dependencies that must also move forward every six months and concludes, "I think it's fair to say that it's a bold choice to use Java 9 or 10."

As a reminder, the aforementioned Reinhold blog post "Moving Java Forward Faster" outlines how the new proposed release train addresses "the tension between developers, who prefer rapid innovation, and enterprises, which prefer stability, and the fact that everyone prefers regular and predictable releases." The following are key points of this new release train approach:

  • "Adopt a strict, time-based model with a new feature release every six months, update releases every quarter, and a long-term support release every three years." (I added the emphasis)
  • Feature Releases (which "contain any type of feature") ship in March and September of each year with the first one being March 2018 (JDK 10 that Colebourne references when he writes, "Java 9 has six weeks to live").
  • Update Releases (which are "strictly limited to fixes of security issues, regressions, and bugs in newer features") occur between the Feature Releases with two Update Releases between each Feature Release and scheduled with quarter periodicity in the months of January, April, July, and October.
  • Long-Term Support Releases are the same as the Feature Release every third year starting in September 2018. Updates for these long-term support releases will be available at least until the next long-term support release and often may be available longer than those three years.
  • Additional details regarding the Java release train can be found at the #javatrain Twitter handle, in the General OpenJDK Discussion distribution list, in the page "Oracle Java SE Support Roadmap," and in the page "Faster and Easier Use and Redistribution of Java SE."
  • It was recently announced that "the public availability of Java SE 8 updates from Oracle has been extended to at least January 2019" and that "Oracle will continue to provide consumers with updates for personal (non-corporate) use of Java SE 8 through at least the end of 2020."

Colebourne is not the only one to warn Java developers to consider the ramifications of moving from Java 8 to Java 9. In the post "Java 9: No Long-Term Support and Twice-Yearly JDK Releases," Carly Yuk writes that "Java 9 will not be entitled to long-term maintenance." Yuk adds that "enterprises that are running applications in products may want to consider waiting for the future long-term release." Paul Krill writes that "Java 9 will not receive long-term support" and Ben Evans has been paraphrased, "Since Oracle has announced that Java 8 will be a long-term support release, supported through 2022, Evans thinks that a lot of applications might stay on Java 8 and not upgrade to Java 9 at all."

There is much to think about when deciding whether to upgrade to Java 9 or not. There is no single "correct" answer as situations, environments, priorities, and uses of Java differ greatly. In general, developers of larger "enterprise" type applications will likely want to only adopt the long-term support releases and developers of smaller applications will likely be willing to adopt feature releases and associated update releases to get access to new features sooner. This ability to choose between "rapid innovation" and supported stable versions is one of the driving motivations for the new release train.

JDK 9: NotNullOrElse Methods Added to Objects Class

JDK 9 added some new methods to the Objects class including two static methods highlighted in this post: requireNonNullElse(T,T) and requireNonNullElseGet​(T obj,Supplier<? extends T> supplier). Both methods make it easier to verify that a given object is not null and to provide an alternative if the provided variable turns out to be null. As such, these methods and the similar methods introduced to Objects in earlier JDK versions [requireNonNull​(T), requireNonNull​(T,String), and requireNonNull​(T,Supplier<String>)] are most likely to be used to implement guard clauses in methods.

The three methods mentioned in the last paragraph that were added to Objects prior to JDK 9 did not allow a "default" value to be used when the object being tested was determined to be null. Instead, each of these three methods throws a NullPointerException when the variable passed to them is null. The two methods added to Objects in JDK 9 do allow a default to be specified that can be returned by the method rather than the method throwing a NullPointerException.

Objects.requireNonNullElse​(T,T) is the most straightforward approach of the two new added methods for specifying a default object to be returned when the provided variable under test is null. An example of applying this method is shown in the next code listing.

Example of Objects.requireNonNullElse​(T,T)

/**
 * Provide instance of {@code Instant} that corresponds to
 * the provided instance of {@code Date}.
 *
 * @param inputDate Instance of {@code Date} for which
 *    corresponding instance of {@code Instant} is desired;
 *    if this is {@code null}, an {@code Instant} representing
 *    "now" will be returned.
 * @return Instance of {@code Instant} extracted from provided
 *    {@Date} that will instead represent "now" if provided
 *    {@code Date} is {@code null}.
 */
public Instant convertDateToInstantWithNowDefault(final Date inputDate)
{
   final Date dateToConvert
      = Objects.requireNonNullElse(inputDate, new Date());
   return dateToConvert.toInstant();
}

In the above example, if the provided variable of type Date is null, a provided default of "now" (based on calling the Date constructor that doesn't accept arguments) is returned instead.

JDK 9 also added the method Objects.requireNonNullElseGet​(T,Supplier<? extends T>) for a similar purpose. This method differs from the previously discussed method in that it accepts a Supplier for providing the default value rather than accepting another object of the same type to serve as the default.

In the highly recommended book Modern Java Recipes, Ken Kousen writes, "One of the primary use cases for Suppliers is to support the concept of deferred execution." After discussing how Supplier is used in the JDK, he adds, "This process of deferred execution can be used in your own code, to ensure that a value is retrieved from the Supplier only when appropriate." My next example demonstrates this.

A highly contrived code listing is shown next and demonstrates use of this method accepting a Supplier.

Example of Objects.requireNonNullElseGet​(T,Supplier<? extends T>)

/**
 * Provide instance of {@code Instant} that corresponds to
 * the provided instance of {@code Date}.
 *
 * @param inputDate Instance of {@code Date} for which
 *    corresponding instance of {@code Instant} is desired;
 *    if this is {@code null}, an {@code Instant} based on
 *    a complicated date calculation will be returned.
 * @return Instance of {@code Instant} extracted from provided
 *    {@Date} that will instead represent a calculated date if
 *    provided {@code Date} is {@code null}.
 */
public Instant convertDateToInstantWithCalculatedDefault(final Date inputDate)
{
   final Date dateToConvert
      = Objects.requireNonNullElseGet(inputDate, () -> calculateDate());
   return dateToConvert.toInstant();
}

The version of the method accepting a Supplier may be advantageous when the code for determining the default is expected to be long-running. In such cases, that long-running method is only executed if the first passed-in argument is null. When the first passed-in argument is not null, the long-running method is not invoked. [By the way, I don't show the implementation of calculateDate() here because it's ridiculously contrived, but suffice it to say that it intentionally takes a very long time to execute.]

The two methods covered in this post make it easy to detect if a particular variable is null and to provide a suitable replacement in its stead when it is null. These are likely to be most often used to implement "guard clauses," but their ability to return a default value could lead to additional use cases as well.

Tuesday, January 30, 2018

Transferring InputStream to OutputStream in JDK 9

One of the minor additions to JDK 9 that can make a sometimes routine task in Java even easier is the addition of the method InputStream.transferTo(OutputStream). This method, as its name suggests, allows for the easy transfer (copy) of bytes from the input stream represented by the object the method is called upon to the output stream provided to that method. Or, as the method's Javadoc comment states, InputStream.transferTo(OutputStream) "reads all bytes from this input stream and writes the bytes to the given output stream in the order that they are read."

There is more to the Javadoc comment on the InputStream.transferTo(OutputStream) method including these statements:

  • "This method does not close either stream."
  • " It is strongly recommended that both streams be promptly closed if an I/O error occurs."

The easiest way to deal with the two concerns shown above that are expressed in the Javadoc comment for the InputStream.transferTo(OutputStream) method is to instantiate both the source InputStream and the target OutputStream in a try-with-resources statement. An example of this is shown in the next code listing.

StreamsTransfer.java: Using InputStream.transferTo(OutputStream)

package dustin.examples.iostreams;

import static java.lang.System.out;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

/**
 * Demonstrate InputStream.transferTo(OutputStream) added in JDK 9.
 */
public class StreamsTransfer
{
   /**
    * Demonstrate use of InputStream.transferTo(OutputStream) using
    * FileInputStream and FileOutputStream implementations of
    * InputStream and OutputStream respectively.
    *
    * @param arguments Command-line arguments: one expected,
    *    which is the name of the input file.
    */
   public static void main(final String[] arguments)
   {
      if (arguments.length < 1)
      {
         out.println("USAGE StreamsTransfer <fileName>");
         System.exit(-1);
      }
      final String fileName = arguments[0];
      try (final InputStream is = new FileInputStream(fileName);
           final OutputStream os = new FileOutputStream(fileName + ".copy"))
      {
         is.transferTo(os);
      }
      catch (IOException exception)
      {
         out.println("Exception encountered: " + exception);
      }
   }
}

The try-with-resources statement in the above code listing opens the two resources (instances of InputStream and OutputStream) and so ensures that they are always closed. The implementation of InputStream used in this example is FileInputStream and the implementation of OutputStream used in this example is FileOutputStream.

Although the file copying implemented in the above example could be more easily accomplished in Java with a different mechanism (such as using one of the overloaded Files.copy methods), the approach shown in the code listing above is only intended for easy illustration and can be generalized to any implementations of InputStream and OutputStream. For another example, Josh Bloch discusses use of InputStream.transferTo(OutputStream) in Item 59 of Effective Java (Third Edition) and his illustration uses URL.openStream() as the InputStream and System.out as the OutputStream.

When the above example is executed, it will copy the provided file to another file with the same name with ".copy" added to the end of the new file's name. The two I/O streams are closed even if an exception occurs during processing of either one.

The addition of InputStream.transferTo(OutputStream) seems to be generally welcomed among the Java development community. Ali Dehghani talks about this method in the post "Least significant bits of Java 9." This method is also included in the post "5 things made easier in Java 9" (which also points out that effectively final variables can now be used in try-with-resources rather than explicitly making them final like I did in my example). The Reddit /r/java subreddit includes an interesting discussion titled "New method in JDK 9: InputStream.transferTo(OutputStream)."

Not everyone is fan of the adding of InputStream.transferTo(OutputStream) in JDK 9. In the post "Java 9: The Good, The Bad, and Private Interface Methods", Yegor Bugayenko describes InputStream as an "already over bloated class" and writes that the addition of InputStream.transferTo(OutputStream) is "one of the most typical mistakes young OOP programmers are making: they make their interfaces big ... just because they need more functionality." He also points that IOUtils.copy(InputStream, OutputStream) was already available via Apache Commons.

The addition of the InputStream.transferTo(OutputStream) method with JDK 9 is a small but sometimes very handy addition to the standard JDK that is especially easy to use in conjunction with the try-with-resources statement.

Tuesday, January 23, 2018

Adding Terms to Javadoc Search with Java 9

There is a relatively old web page called "Proposed Javadoc Tags" that appears to have originally been written in conjunction with Javadoc 1.2 that lists "tags that Sun may implement in Javadoc someday." The tags in this list are @category, @example, @tutorial, @index, @exclude, @todo, @internal, @obsolete, and @threadsafety. One of these tags, @index, has moved from "Proposed Tags" to "Standard Tags" with its inclusion in Java 9. The Java 9 Javadoc tool documentation states that the @index tag is used to specify an indexed "search term or a phrase" that can be searched for in Java 9's new Javadoc Search feature.

The ability to add terms for searching in Javadoc generated documentation has been desired for some time as demonstrated by the existence of JDK-4034228 ("stddoclet: Add @index doc-comment tag for generating an index from common words"), JDK-4279638 ("Javadoc comments: Need ability to tag words for inclusion in the API index"), and JDK-4100717 ("Allow user-specified index entries"). JEP 225 ("Javadoc Search") was used to "add a search box to API documentation generated by the standard doclet that can be used to search for program elements and tagged words and phrases within the documentation."

Javadoc in Java 9 and later will automatically include several constructs in the "Search" that can be performed from the generated HTML output. These searchable by default strings are those based on methods' names, members' names, types' names, packages' names, and modules' names. The advantage offered by @index is that phrases or search terms not built into the names of these just-listed constructs can be explicitly to the searched index.

There are several examples of where the ability to add customized text for searching Javadoc generated documentation can be useful. The Javadoc tool documentation references the "domain-specific term ulps" ("units in the last place") and explains that although "ulps is used throughout the java.lang.Math class," it "doesn't appear in any class or method declaration names." Using @index would allow the API designers of the Math class to add "ulps" to the searchable index to help people find the Math class when searching for "ulps." In Effective Java's Third Edition, Josh Bloch references another example of where Javadoc {@index} might be useful. In Item 56, Bloch cites an example using {@index IEEE 754} ("IEEE Standard for Floating-Point Arithmetic").

I recently ran into a case in the JDK where I thought use of {@index} would be appropriate. I posted recently on the Dual-Pivot Quicksort, but realized that one does not find any matches for that term when searching the Javadoc-generated output. It seems like it would be useful to add terms such as "Dual Pivot Quicksort" and "Mergesort" to the Javadoc search index via {@index}.

Unfortunately, having spaces in the text embedded in the {@index } tag seems to result in only the terms before the first space showing up in the rendered HTML (and being the only portions that can be searched). To demonstrate this, the following ridiculously contrived Java code contains three {@index} Javadoc tags representative of the three examples just discussed.

Java Code Using {@index} in Its Documentation

package dustin.examples.javadoc;

/**
 * Used to demonstrate use of JDK 9's Javadoc tool
 * "@index" tag.
 */
public class JavadocIndexDemonstrator
{
   /**
    * This method complies with the {@index IEEE 754} standard.
    */
   public void doEffectiveJava3Example()
   {
   }

   /**
    * Accuracy of the floating-point Math methods is measured in
    * terms of {@index ulps}, "units in the last place."
    */
   public void doMathUlpsExample()
   {
   }

   /**
    * This method uses a version of the {@index Dual-Pivot Quicksort}.
    */
   public void doDualPivotQuicksort()
   {
   }
}

When the Javadoc tool is executed against the above code on my Windows 10 machine in Java 9.0.4, the generated HTML page looks like this:

The "754" is missing in the generated HTML after "IEEE" and the "Quicksort" is missing after "Dual-Pivot" in the methods' documentation. The next code listing shows the generated HTML source code for these pieces with missing text.

HTML Source

<div class="block">This method uses a version of the <a id="Dual-Pivot" class="searchTagResult">Dual-Pivot</a>.</div>
 . . .
<div class="block">This method complies with the <a id="IEEE" class="searchTagResult">IEEE</a> standard.</div>

From the HTML output just shown, it becomes apparent why only the text before the first space appears in the page and is searchable. The "id" attribute associated with the "searchTagResult" class for each searchable entry consists of the searchable string. Because HTML "id" attributes cannot have spaces, only the characters up to the first space can be used for the "id" value.

Because spaces are not allowed in the "id" attributes, one of the following work-arounds would need to be used when dealing with multiple words in a single phrase for which search is desired.

  1. Remove spaces
    • "{@index IEEE 754}" becomes "{@index IEEE754}"
    • "{@index Dual-Pivot Quicksort}" becomes "{@index Dual-PivotQuicksort}"
  2. Replace spaces with allowable character (for example, hyphen)
    • "{@index IEEE 754}" becomes "{@index IEEE-754}"
    • "{@index Dual-Pivot Quicksort}" becomes "{@index Dual-Pivot-Quicksort}"
  3. Use separate {@index} for each word in phrase
    • "{@index IEEE 754}" becomes "{@index IEEE} {@index 754}"
    • "{@index Dual-Pivot Quicksort}" becomes "{@index Dual-Pivot} {@index Quicksort}"
  4. Use {@index} only on most important terms in phrase
    • "{@index Dual-Pivot Quicksort}" becomes "{@index Dual-Pivot} Quicksort"
  5. Represent multiple word phrase with common single word representation
    • This is why "ulps" in the Javadoc tool documentation works well rather than "units in the last place."

The "Motivation" section of JEP 225 ("Javadoc Search") nicely summarizes the benefits of this ability to search for terms in Javadoc:

The API documentation pages generated by the standard doclet can be hard to navigate if you're not already familiar with their layout. An external search engine can be used, but that may lead to an out-dated or irrelevant page. The browser's built-in search function can be used, but that is limited to searching within the current page rather than an entire body of documentation.

Although adding search capability to Javadoc-generated documentation is a minor addition in Java 9, it can be used to make documentation of one's Java code more useful to other developers and users of that code.

Wednesday, January 10, 2018

The Highly Useful Java ChronoUnit Enum

Several years ago, I published the blog post "The Highly Useful Java TimeUnit Enum" that looked at the TimeUnit enum introduced with JDK 5. JDK 8 introduced a newer enum, ChronoUnit, that is better suited than TimeUnit for contexts other than concurrency such as date/time manipulations.

Located in the java.time.temporal package, the ChronoUnit class implements the TemporalUnit interface, an interface used extensively in the highly desired JDK 8-introduced Date/Time API. The blog post "Days Between Dates in Java 8" demonstrates use of this class to calculate periods of time between two instances of Temporal.

The blog post "Java 9. Where 'forever' is hard coded." looks at "two new methods in the TimeUnit class" for JDK 9. These methods, toChronoUnit() and of(ChronoUnit), support translation of TimeUnit to a ChronoUnit and translation of ChronoUnit to TimeUnit. Not all values in ChronoUnit can be translated to an equivalent in TimeUnit, in which cases an IllegalArgumentException is thrown.

The Javadoc comments on each value in ChronoUnit describe what unit of time each value represents. However, it's interesting to me to see what Duration is returned for each value in ChronoUnit. The following code snippet will write these Duration's toString() representations to standard output for all values in the ChronoUnit enum.

Displaying Durations of ChronoUnits

for (final ChronoUnit unit : ChronoUnit.values())
{
   final Duration duration = unit.getDuration();
   out.println(unit + ": " + duration + " (" + duration.getSeconds() + " seconds)");
}

When executed, the above code produces the following output:

Nanos: PT0.000000001S (0 seconds)
Micros: PT0.000001S (0 seconds)
Millis: PT0.001S (0 seconds)
Seconds: PT1S (1 seconds)
Minutes: PT1M (60 seconds)
Hours: PT1H (3600 seconds)
HalfDays: PT12H (43200 seconds)
Days: PT24H (86400 seconds)
Weeks: PT168H (604800 seconds)
Months: PT730H29M6S (2629746 seconds)
Years: PT8765H49M12S (31556952 seconds)
Decades: PT87658H12M (315569520 seconds)
Centuries: PT876582H (3155695200 seconds)
Millennia: PT8765820H (31556952000 seconds)
Eras: PT8765820000000H (31556952000000000 seconds)
Forever: PT2562047788015215H30M7.999999999S (9223372036854775807 seconds)

The "PT" prefix on each of the Duration's string representations shown above indicates that the representation is a "period" duration designation ("P") and a "time" designation ("T") per the ISO-8601 standard. The "S", "M", and "H" are seconds, minutes, and hours respectively. The values of ChronoUnit that represent time units less than a second (NANOS, MICROS, and MILLIS) show "0 seconds" because they are less than 1 second and the returned value is an integral long.

The Javadoc comments on each value defined in the ChronoUnit class are well written. They follow what in my mind is a Javadoc "best practice": place a concise but informative initial sentence in the Javadoc to show up in the "Method Summary" section of the generated HTML page and place additional useful details in sentences after that initial summary sentence. For example, the Javadoc comment for ChronoUnit.ERAS states, "Unit that represents the concept of an era. The ISO calendar system doesn't have eras thus it is impossible to add an era to a date or date-time. The estimated duration of the era is artificially defined as 1,000,000,000 Years. When used with other calendar systems there are no restrictions on the unit." The bolded sentence (I added that emphasis) is what shows up in the "Method Summary" and the entire text shown here is displayed above the method in its complete explanation.

One of the more interesting values in the ChronoUnit enum is FOREVER. As the output of the code listing above demonstrated, the FOREVER value has a Duration of "PT2562047788015215H30M7.999999999S", corresponding to 2562047788015215 hours, 30 minutes, and 7.999999999 seconds. Or, as Grzegorz Gajos expresses it, "Java defines forever as 9 223 372 036 854 775 807 seconds. Which is 2.92277266 × 1011 years. Better be sure to schedule Java upgrade in your application before times run out."

When would ChronoUnit.FOREVER be useful? Its Javadoc-based description explains its primary reason for existence: "Artificial unit that represents the concept of forever. This is primarily used with TemporalField to represent unbounded fields such as the year or era. The estimated duration of the era is artificially defined as the largest duration supported by Duration."

TimeUnit is a useful enum for working with Java's concurrency constructs and could be used in contexts other than concurrency as long as some severe limitations for these other contexts were considered. The JDK 8-introduced ChronoUnit is a better enum for these non-concurrency contexts and is especially designed for use with the JDK 8 Date/Time API.

Thursday, September 21, 2017

JDK 9 Released Today

The big news in Java today is, of course, the release of JDK 9 in General Availability. Mark Reinhold starts his message JDK 9: General Availability with the statement, "I'm pleased -- nay, thrilled! -- to announce that JDK 9 is now Generally Available."

The Reinhold post adds that in addition to Jigsaw, there are "many other excellent additions and improvements." He lists them with links that are reproduced here:

It will be interesting to see what people learn and share from using JDK 9 over the next several months. We've already seen numerous resources based on early releases of JDK 9, but I'd expect usage and consequential lessons learned to pick up with JDK 9 in General Availability. Recent posts that provide examples of this include Java 9, Jigsaw, JPMS, and Modules: A Personal Exploration and JDK 9: XXtra Command Line Options.

Additional Resources

Monday, June 5, 2017

jhsdb: A New Tool for JDK 9

I like to use the command-line tools provided with the JDK in the early steps of analyzing performance and other issues with Java-based applications and have blogged on tools such as jcmd, jps, jstat, jinfo, jhat and jmap, jrunscript, jstack, and jdeps. JDK 9 is bringing new command-line tools with multiple tools specifically related to new JDK 9 features such as modularity (jlink and jmod) and enhanced deprecation (jdeprscan). In this post, I focus on a new command-line tool delivered with JDK 9 for dealing with performance and serviceability issues: jhsdb.

The jhsdb tool is described on its Oracle JDK 9 Documentation Early Access page, "You use the jhsdb tool to attach to a Java process or to launch a postmortem debugger to analyze the content of a core-dump from a crashed Java Virtual Machine (JVM)." The tool comes with several "modes" and several of these modes correspond in name and function with individual command-line tools available in previous JDK distributions. The jhsdb tool not only provides a single tool that encompasses functionality of multiple other tools, but it also provides a single, consistent approach to applying these different functions. For example, the jhsdb command-line syntax for getting help for each of the "modes" is identical.

The jhsdb tool can be attached and applied to a running JVM (including one that is hanging) via its process identifier (PID) similar to how several other tools (including jcmd) work. The jhsdb tool can also be used to analyze core information associated with a crashed JVM if the core file and executable are provided. As an example of the consistency jhsdb provides, all of its mode support the "common options" --pid (to specify target JVM's process ID), --exe (to specify target executable), --core (to specify target core dump file), and --help (to display options specific to each mode).

The next series of snapshots demonstrates use of the --help option with the main jhsdb command and with each of several of jhsdb's "modes." One observation that can be made is that the common options --pid, --core, and --exe are offered by all the modes. The obvious inference from this is that the specific functions supported in each mode are those other than those "common" options.

jhsdb "jstack" Mode

The --help for the jhsdb mode jstack has two specific functionality options: --locks and --mixed. These are demonstrated in the following two screen snapshots.

The screen snapshots just shown demonstrate that the jstack mode of the jhsdb tool provides us with deadlock detection details, information on thread locks, and an overview of the native frames and Java frames.

jhsdb "jmap" Mode

The --help for jhsdb mode jmap shows several functions supported by that mode. When jhsdb jmap is executed with only the --pid or only with the --exe/--core combination, the output is similar to that provided by the Linux pmap command.

As one would expect, the jmap mode of the jhsdb provides functions similar to those provided by the separate but similarly named jmap command. These include heap dump (--heap), class histogram (--histo), classloader statistics (--clstats), and finalizer information (--finalizerinfo) and are demonstrated in the following four screen snapshots.

jhsdb "jinfo" Mode

Not surprisingly, the jinfo mode of the jhsdb command provides functionality that overlaps with that provided by the jinfo command. Specifically, the jhsdb's jinfo mode allows one to see the targeted JVM's flags and system properties.

There are three main options used with jhsdb jinfo: --flags to see JVM flags, --sysprops to see the system properties, or no argument to see both the flags and the system properties. The next two screen snapshots demonstrate use of jhsdb jinfo --flags and jhsdb jinfo --sysprops. Running jhsdb jinfo without any arguments shows the system properties first followed by the flags, but is not shown here.

jhsdb "jsnap" Mode

The jhsdb mode jsnap provides access to information previously provided by the internal class sun.jvm.hotspot.tools.JSnap which was previously available in lib/sa-jdi.jar and which has been added to jhdsdb for JDK 9. There are two options for output from jhsdb jsnap based on whether no mode-specific argument is provided or if the --all mode-specific argument is provided. The next two screen snapshots demonstrate these two options.

These screenshots demonstrate that jhsdb jsnap with no mode-specific option lists information such as events related to threads and class loading/unloading along with core JVM properties. Adding the --all option lists these same properties, but in addition adds far more properties and, according to the documentation, "Prints all performance counters."

By the way, Marcus Hirt's Using the JVM Performance Counters provides an interesting look at how to apply JMX and custom MBeans to achieve a tool "similar to the PerformanceCounters MBean available in JRockit." I believe that jhsdb jsnap --all brings simple ability to see the same type of information in HotSpot as Hirt talked about being available in JRockit with jrcmd -l.

jhsdb Debug Modes

The three jhsdb modes hsdb (graphical user interface for interactive debugging), clhsdb (command-line interface for interactive debugging), and debugd (remote debug server) are related to debug operations. I may take a closer look at these modes in a future post, but for now I simply show some screen snapshots that demonstrate the graphical interaction using jhsdb hsdb. The GUI was started with jhsdb hsdb --pid <pid> and most of the options displayed here were run by selecting the specific menu option under "Tools".

As can be seen in the article HotSpot's Hidden Treasure, the serviceability debugger GUI has been available before JDK 9 and jhsdb, but this article also shows how much more difficult it was to find and start this tool before JDK 9's introduction of jhsdb.

Relationship of jhsdb to jcmd and to Other Command-line JDK Tools

I summarized the relationship of general-purpose tool jcmd to other JDK-provided command-line tools in the blog post jcmd: One JDK Command-Line Tool to Rule Them All. I adapt that table here to add jhsdb to the mix.

FunctionalityjhsdbjcmdSimilar Tool
Listing Java Processes N/A1 jcmd jps -lm
Heap Dumps jhsdb jmap --binaryheap jcmd <pid> GC.heap_dump jmap -dump <pid>
Heap Usage Histogram jhsdb jmap --histo jcmd <pid> GC.class_histogram jmap -histo <pid>
Thread Dump jhsdb jstack --locks
(subset of locked thread frames)
jcmd <pid> Thread.print jstack <pid>
List System Properties jhsdb jinfo --sysprops jcmd <pid> VM.system_properties jinfo -sysprops <pid>
List VM Flags jhsdb jinfo --flags jcmd <pid> VM.flags jinfo -flags <pid>

1 You use jcmd or jps -lm to identify PID upon which to have jhsdb, jcmd, and many other tools act if working against a running JVM. I used jcmd in this post to identify the PID, but the current jhsdb documentation demonstrates using jps to acquire the JVM PID.

The jhsdb tool is a mostly command-line tool that does also have an optional interactive GUI available that supports reporting of many of the commonly desired attributes of a JVM that is hung or has crashed. It provides a consistent interface across its modes and the command-line interaction allows for interactive help requests such that very little syntax must be known or remembered before applying the tool. If one can remember "jhsdb", one can start using the tool effectively. The jhsdb tool is new to JDK 9, but brings functionality into one tool that was previously available from several different tools.