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

Friday, January 31, 2020

Source Code for Effective Java Third Edition Updated to Use Newer Features

Those who have read the Third Edition of Effective Java are likely aware of the source code associated with that book available on GitHub. The jbloch/effective-java-3e-source-code project has 1700+ stars and has been forked nearly 800 times as of this writing. The version of Java featured in the Third Edition of Effective Java is largely JDK 8 with some coverage of JDK 9 (see my earlier post for details on what is covered in this third edition).

Much has been added to the JDK since the publication of the Third Edition of Effective Java and many new releases have arrived with the faster 6-month cadence. Given this, I was particularly interested to see in an amber-spec-experts mailing list post that Rémi Forax has forked jbloch/effective-java-3e-source-code into the GitHub project forax/effective-java-3e-source-code that has "taken the source of Effective Java (3rd Ed) and change them to use var, switch expression, records and the instanceof with the type test pattern."

There are several things that I like about the idea of refreshing examples from Effective Java (Third Edition) to use newer features:

  • Developers can see how to apply effective Java practices using recently released features.
  • Developers can view the differences between the JDK 8/9 versions and the newer versions to see how new constructs replace older constructs and thus gain a better understanding of the newer constructs.
  • It is useful to see some of the changes when deciding whether a particular change to use a newer construct really helps with code readability in a given situation.

The main page for the forked forax/effective-java-3e-source-code (README.md) states, "The source code have been updated to use new constructs available since Java 9, the version used by the 3rd edition." That page then provides bullets on the types of new constructs applied to the source code with links to each new construct's associated JDK Enhancement Proposal (JEP).

As of this writing, Commit 275eef87e4661f7f1edc41f4730cecf7a1096a97 is the main commit of interest. It covers changes to 113 files. I'll call out a few specific changes here to illustrate the types of changes applied (some of which are to apply preferred constructs that were available even before JDK 9):

Conclusion

The ability to view changes to the original source code associated with the Third Edition of Effective Java to accommodate new language constructs is highly useful in terms of learning about the new constructs and how they relate to or replace old constructs and in deciding if the differences are desirable in different situations.

Monday, June 10, 2019

JDK 13: VM.events Added to jcmd

CSR (Compatibility and Specification Review) JDK-8224601 ["Provide VM.events diagnostic command"] is implemented in JDK 13 as of JDK 13 Early-Access Build #24 (dated 2019/6/6) and was added via Enhancement JDK-8224600 ["Provide VM.events command"]. The CSR's "Summary" describes this enhancement: "Add a VM.events command to jcmd to display event logs." The CSR's "Solution" states, "Add a command to jcmd to print out event logs. The proposed name is 'VM.events'."

The "Problem" section of CSR JDK-8224601 explains the value achieved from adding VM.events to the already multi-functioning jcmd: "Event logs are a valuable problem analysis tool. Right now the only way to see them is via hs-err file in case the VM died, or as part of the VM.info output."

To demonstrate jcmd's new VM.events in action, I downloaded JDK 13 Early Access Build #24 and used it to compile a simple, contrived Java application that I could then run jcmd tool delivered with that same JDK 13 Early Access Build #24 against.

The first screen snapshot shown here demonstrates using jcmd to detect the PID of the simple Java application and using jcmd <pid> help to see what jcmd options are available for that particular running Java process. The presence of VM.events is highlighted.

The next screen snapshot demonstrates applying jcmd <pid> help VM.events to see the usage (including available options) for the newly added VM.events command.

The final screen snapshot demonstrates application of jcmd's new VM.events command by showing the top (most) portion of the output from running that command without any options.

The options for the VM.events command are to narrow down results to a specified log to be printed or to limit the number of events shown. By not specifying any options, I was implicitly requesting the default of all logs and all events.

In the last displayed screen snapshot, we could see that types of JVM events rendered in the output include "compilation events", "deoptimization events", garbage collection events, classes unloaded, classes redefined, and classes loaded.

I have been a big fan of jcmd for a number of years and believe it is still generally an underappreciated command-line tool for many Java developers. The addition of the VM.events command in JDK 13 makes the tool even more useful for diagnosing a wider variety of issues.

Saturday, April 27, 2019

Two JEPs Proposed for JDK 13: Enhancing AppCDS and ZGC

Two JDK Enhancement Proposals (JEPs) were proposed for JDK 13 this week on the OpenJDK jdk-dev mailing list. Mark Reinhold posted these proposals in messages with titles that indicate the JEP topic: "JEP proposed to target JDK 13: 350: Dynamic CDS Archives" and "JEP proposed to target JDK 13: 351: ZGC: Uncommit Unused Memory".

The "Summary" of proposed JEP 350 ["Dynamic CDS Archives"] states, "Extend application class-data sharing to allow the dynamic archiving of classes at the end of Java application execution. The archived classes will include all loaded application classes and library classes that are not present in the default, base-layer CDS archive." JEP 310 introduced "Application Class-Data Sharing" (AKA "AppCDS") via JDK-8185996 and in conjunction with JDK 10.

JEP 351 ["ZGC: Uncommit Unused Memory"]'s "Summary" section states simply, "Enhance ZGC to return unused heap memory to the operating system." The "Motivation" section adds more background details, "ZGC does not currently uncommit and return memory to the operating system, even when that memory has been unused for a long time. This behavior is not optimal for all types of applications and environments, especially those where memory footprint is a concern." "ZGC" refers to the "Z Garbage Collector" and more details regarding it can be found on the OpenJDK ZGC page and on the ZGC Wiki page. The main project page states, "The goal of this project is to create a scalable low latency garbage collector capable of handling heaps ranging from a few gigabytes to multi terabytes in size, with GC pause times not exceeding 10ms."

Both proposed JEPs will be officially targeted for JDK 13 next week if no objections are raised or if any raised objections are "satisfactorily answered."

Monday, April 22, 2019

OpenJDK on GitHub

Project Skara was created "to ... investigate alternative SCM and code review options for the JDK source code, including options based upon Git rather than Mercurial, and including options hosted by third parties." The OpenJDK skara-dev mailing list included a post from Robin Westberg last week that announced, "We have added some additional read-only mirrors of a few different OpenJDK project repositories to the https://github.com/openjdk group..."

The read only OpenJDK repositories on GitHub will likely be more convenient for developers wanting to take advantage of the "open source" nature of OpenJDK to take a peek at its internals. More developers are likely to be comfortable with Git than with Mercurial. The GitHub-hosted repositories make it even easier to clone a given repository or to even fork it.

As of this writing, there are currently nine public repositories hosted on the OpenJDK GitHub site:

This is not the first time the OpenJDK has been mirrored on GitHub. There are 11 repositories in "Mirror of OpenJDK repositories": jdk (2017), jdk7u-jdk (2012), jdk7u (2012), openjdk-mirror-meta (2015), corba (2015), jaxp (2015), jdk7u-langtools (2012), jdk7u-jaxws (2012), jdk7u-jaxp (2012), jdk7u-hotspot (2012), and jdk7u-corba (2012). There is also a Project-Skara/jdk that was last updated in August 2018.

Project Skara is not finished and active development of OpenJDK continues on the Mercurial-based version control system. However, the availability of important OpenJDK repositories on GitHub should make it more convenient for Java developers to analyze OpenJDK source code.

Saturday, April 13, 2019

Viewing TLS Configuration with JDK 13

JDK 13 Early Access Build 16 is now available and one of the interesting additions it brings is the ability to have the keytool command-line tool display the current system's TLS configuration information. This is easier than trying to find supported TLS information in separate documentation and match that information to one's JDK vendor and version.

To see the TLS configuration details with JDK 13 Early Access Build 16, one simply needs to enter keytool -showinfo -tls on the command line, but I'll describe a few more things about this command in this post.

The next screen snapshot shows that the JDK I'm using for my examples is the JDK 13 Early Access Build 16 and demonstrates that the keytool usage now shows the tool including the -showinfo command.

Simply entering keytool without any commands or options results in the usage statement shown in the screen snapshot. The description for the -showinfo command is, "Displays security related information."

The next screen snapshot demonstrates the hint that is provided when one tries to use keytool -showinfo without an option ('Try "keytool -showinfo -tls".'). The image also shows the options associated with the keytool command -showinfo that are displayed when keytool -showinfo --help is entered.

The --help option used with the -showinfo command displays a -v option, but I found on my Windows installation that this -v option does not provide any additional value over simply using the -tls option. The next screen snapshot shows the results of attempting to use the -v option alone (without the -tls option):

When trying to use -v along with the keytool command -showinfo, we get an error message and a recommendation to try keytool -showinfo -tls instead. That does indeed work better as shown in the next screen snapshot that only shows partial results of what's returned.

The output from running keytool -showinfo -tls lists "Enabled Protocols" and "Enabled Cipher Suites." In this case, we see that the "enabled protocols" are TLSv1.3, TLSv1.2, TLSv1.1, and TLSv1.

I found it interesting to look at the code changes required to implement this new command and option for keytool. The implementation uses the JDK's javax.net.ssl.SSLContext class's getDefault() method to acquire the "default SSL context." The returned SSLContext instance's getSocketFactory() method is invoked and the createSocket() method is called on the returned instance of javax.net.ssl.SSLSocketFactory. The returned instance of javax.net.ssl.SSLSocket has two methods getEnabledProtocols() and getEnabledCipherSuites() that return the values shown above in the output from running keytool -showinfo -tls.

The addition to JDK 13's keytool command-line tool of the -showinfo command with its -tls option is available as of Early Access Build 16 and was delivered via JDK-8219861. It's also worth noting that JDK-8204636 may eventually lead to improvements for JDK's TLS 1.3 support.

Saturday, March 23, 2019

Better Default NullPointerException Messages Coming to Java?

I have recently been interested in a February 2019 into March 2019 discussion on the OpenJDK core-libs-dev mailing list regarding addressing the lack of a detailed message associated with a NullPointerException that was thrown after being instantiated with its no-arguments constructor. This is an issue I've run into frequently when using Java and which has even caused me to change code in a few cases to better deal with the issue.

In many cases, NullPointerException (NPE) can be one of the easier exceptions to resolve (or at least diagnose what was null) if only one possible source of a NullPointerException exists in the statement and if line numbers are available in the stack trace (not compiled with -g:none).

Although it is especially challenging for those new to Java, a NullPointerException with no message can be a disappointing experience even for experienced Java developers in certain cases. The most obvious case when having no message associated with the NullPointerException is when there are multiple candidates in a given statement that might be throwing the NullPointerException. One example of this case is calling methods upon each previous method's return object in a manner such as this: getA().getB().getC()... where each of the methods potentially returns null. Another example is when multiple arguments of primitive data types to a method (or constructor) might lead to a NullPointerException if the caller passes a null to to that method that is dereferenced to be a primitive.

Enhancement JDK-8218628 ("Add detailed message to NullPointerException describing what is null.") addresses some of these cases. The description of this enhancement states, "When getting a NPE it is often hard to determine which reference in an expression had been null. This change adds a message telling this." This enhancement also provides several examples of Java statements that typically result in NullPointerException with potentially frustrating lack of detail. I have captured similar cases to these examples in the GitHub-hosted class NpeDemo (see this version to match to line numbers in output below). When those demonstration examples are executed (they all intentionally throw NPEs), the output appears as shown in the following when compiled with default settings (full stack information still available):

=========================================
| #1: Element [0] on null boolean array |
=========================================

java.lang.NullPointerException
 at dustin.examples.npe.NpeDemo.demonstrateFirstExampleIndexAccessOnNullBooleanArray(NpeDemo.java:37)
 at dustin.examples.npe.NpeDemo.demonstrateJdk8218628Examples(NpeDemo.java:179)
 at dustin.examples.npe.NpeDemo.main(NpeDemo.java:310)

=================================
| #2: .length on null boolean[] |
=================================

java.lang.NullPointerException
 at dustin.examples.npe.NpeDemo.demonstrateSecondExampleLengthOnNullBooleanArray(NpeDemo.java:59)
 at dustin.examples.npe.NpeDemo.demonstrateJdk8218628Examples(NpeDemo.java:180)
 at dustin.examples.npe.NpeDemo.main(NpeDemo.java:310)

=======================================
| #3: Assigning float to null float[] |
=======================================

java.lang.NullPointerException
 at dustin.examples.npe.NpeDemo.demonstrateThirdExampleAssigningValueToElementOfNullFloatArray(NpeDemo.java:80)
 at dustin.examples.npe.NpeDemo.demonstrateJdk8218628Examples(NpeDemo.java:181)
 at dustin.examples.npe.NpeDemo.main(NpeDemo.java:310)

======================================
| #4: Accessing field on null object |
======================================

java.lang.NullPointerException
 at dustin.examples.npe.NpeDemo.demonstrateFourthExampleAccessInstanceFieldOfNullObject(NpeDemo.java:101)
 at dustin.examples.npe.NpeDemo.demonstrateJdk8218628Examples(NpeDemo.java:182)
 at dustin.examples.npe.NpeDemo.main(NpeDemo.java:310)

===================
| #5: throw null; |
===================

java.lang.NullPointerException
 at dustin.examples.npe.NpeDemo.demonstrateFifthExampleThrowingConstantNull(NpeDemo.java:121)
 at dustin.examples.npe.NpeDemo.demonstrateJdk8218628Examples(NpeDemo.java:183)
 at dustin.examples.npe.NpeDemo.main(NpeDemo.java:310)

================================================
| #6: Method invocation on null instance field |
================================================

java.lang.NullPointerException
 at dustin.examples.npe.NpeDemo.demonstrateSixthExampleMethodInvocationOnNullInstanceField(NpeDemo.java:141)
 at dustin.examples.npe.NpeDemo.demonstrateJdk8218628Examples(NpeDemo.java:184)
 at dustin.examples.npe.NpeDemo.main(NpeDemo.java:310)

=============================================
| #7: synchronized() on null instance field |
=============================================

java.lang.NullPointerException
 at dustin.examples.npe.NpeDemo.demonstrateSeventhExampleSynchronizedNullInstanceField(NpeDemo.java:161)
 at dustin.examples.npe.NpeDemo.demonstrateJdk8218628Examples(NpeDemo.java:185)
 at dustin.examples.npe.NpeDemo.main(NpeDemo.java:310)

==========================================================================
| <<< Null Lost in Long Series of Method Invocations in Single Statement |
==========================================================================

java.lang.NullPointerException
 at dustin.examples.npe.NpeDemo.demonstrateNullLostInSeriesOfMethodInvocationsInSingleStatement(NpeDemo.java:198)
 at dustin.examples.npe.NpeDemo.main(NpeDemo.java:311)

=======================================================
| <<< Null Lost in Dereferenced Constructor Arguments |
=======================================================

java.lang.NullPointerException
 at dustin.examples.npe.NpeDemo.demonstrateNullLostInConstructorAcceptingMultiplePotentiallyNullArgumentsDereferenced(NpeDemo.java:226)
 at dustin.examples.npe.NpeDemo.main(NpeDemo.java:312)

==================================================
| <<< Null Lost in Dereferenced Method Arguments |
==================================================

java.lang.NullPointerException
 at dustin.examples.npe.NpeDemo.demonstrateNullLostInMethodAcceptingMultiplePotentiallyNullArgumentsDereferenced(NpeDemo.java:254)
 at dustin.examples.npe.NpeDemo.main(NpeDemo.java:313)

There is no message provided with any of the NullPointerExceptions shown in the examples above. However, in these cases, the culprit is relatively easy to identify because the methods they occur in are small and there are line numbers that point directly to where the NPE was thrown. These would be more difficult to identify if there were no line numbers (source compiled with -g:none) and the methods were long (multiple lines in which NPEs could be thrown) or there were overloaded versions of the method with the same name.

Had the code been compiled with -g:none, there would be no class name or line number shown in the stack traces [would just list (Unknown Source) instead of (file name:line number)] and it could be trickier to detect where the NPE was thrown, especially if thrown from a lengthy method with many candidates for NPEs or from a method that was overloaded multiple times in the same class such that method name alone is not as helpful.

Some of the examples demonstrated above feature NPEs that are difficult to identify even when one knows the line number because there are so many potential throwers of NPE on that line. Changes such as those proposed by JDK-8218628 would be most welcome in these cases.

Although a solution was implemented for JDK-8218628, it has since been decided that there are enough considerations involved to justify a JDK Enhancement Proposal (JEP) to work out more design and implementation details. This JEP is JDK-8220715 ("Add detailed message to NullPointerException describing what is null") and its "Summary" states, "NullPointerExceptions are freqently encountered developing or maintaining a Java application. NullPointerExceptions often don't contain a message. This complicates finding the cause of the exception. This JEP proposes to enhance the exception text to tell what was null and which action failed."

JEP JDK-8220715 also provides detailed description of the proposed basic algorithm for computing a message for NPE when one is not explicitly provided. The text points out that when a NullPointerException is thrown in its example, "the original Java code is not available," but the information is still "stored in the 'backtrace' field of an exception object" which is "a field private to the jvm implementation."

JEP JDK-8220715 highlights that "computing the NullPointerException message proposed here is a considerable overhead," but addresses that by proposing "delay computing the message until it is actually accessed." In other words, the "default" NPE message would only be calculated if an explicit one had not been provided when the NullPointerException was instantiated.

The "Alternatives" section of JEP JDK-8220715 states that "the current proposal is to implement this in the Java runtime in C++ accessing directly the available datastructures in the metaspace." The section considers some alternatives to this approach (such as implementing it via a JDK library such as StackWalker) and explains why the proposed approach may be preferable to the alternatives.

For more background details related to the proposed enhancements to NullPointerException messages, see the OpenJDK core-libs-dev mailing list. Here are some posts from that discussion that may be of interest with excerpt shown from each post:

  • Goetz Lindenmaier: "... since Java 5, our internal VM reports verbose null pointer exception messages. I would like to contribute this feature to OpenJDK. ... The messages are generated by parsing the bytecodes. For not to have any overhead when the NPE is allocated, the message is only generated when it is accessed by getMessage() or serialization. For this I added a field to NPE to indicate that the message still needs to be computed lazily."
  • Christoph Langer: "... thanks for bringing this into OpenJDK finally. I know of people that'll be quite happy about this feature."
  • Peter Levart: "Make sure to initialize the NPE_MESSAGE_PENDING to a new String("something") or else you may be sharing this constant reference with somebody else via string interning..."
  • Andrew Dinn: "Also, if you want your message to reflect the bytecode that is actually in use when the exception occurs then you really need to do it by pulling the bytecodes out of the method metadata. The bytecode returned by JvmtiClassFileReconstitutor will not include any bytecode changes that were installed by a ClassFileTransformer. However, this is a potential can of worms because old and new versions of a method and associated bytecode can exist at the same time. You need to be sure which version of the method and, hence, bytecode the exception was generated from. If you are trying to do this from Java by calling into the JVM then I think you are going to have problems."
  • Goetz Lindenmaier: "The original implementation is C++ and walks the metaspace given the method* and BCI where the exception occurred. So it uses only data already sitting in memory. See JVM_GetExtendedNPEMessage() in jvm.cpp. The idea was to implement this in Java using StackWalker and ASM. If I had the right bytecodes, and the right starting point, ASM would be helpful to implement the analysis I think."
  • Mandy Chung: "We all think that improving NPE message is a useful enhancement for the platform and helps developers to tell what causes NPE. ... This would get the discussion on the proposal feature and then the discussion of the best way to to implement it in the VM, library, or combination."
  • Maurizio Cimadamore: "... this enhancement will be a great addition to our platform ... I also think that the design space for such an enhancement is non trivial, and would best be explored (and captured!) in a medium that is something other than a patch."
  • Goetz Lindenmaier: "... better wording of the messages ... Especially look at the first few messages, they point out the usefulness of this change. They precisely say what was null in a chain of dereferences."
  • Maurizio Cimadamore: "... please find the attached ASM-based patch. It is just a PoC, as such it does not provide as fine-grained messages as the one discussed in the RFE/JEP, but can be enhanced to cover custom debugging attribute ..."

There are numerous other posts in the threads and the posts above are samples of the discussion.

Having better "default" information associated with NPEs will be a welcome addition. JDK-8218628 is currently associated with JDK 13, but now that JDK-8220715 exists, it may be a bit less certain whether this will be associated with JDK 13. A draft JEP has been written for this, but as a draft JEP, it is not yet targeted to a particular JDK release.

Tuesday, March 19, 2019

Java 12 General Availability

Mark Reinhold announced today that "JDK 12, the reference implementation of Java 12, is now Generally Available." In that announcement, Reinhold stated that "GPL-licensed OpenJDK builds from Oracle are available" at https://jdk.java.net/12 and that "builds from other implementors will no doubt be available soon." Reinhold's post also summarized the eight features delivered with JDK 12:

  • 189: Shenandoah: A Low-Pause-Time Garbage Collector (Experimental)
  • 230: Microbenchmark Suite
  • 325: Switch Expressions (Preview)
  • 334: JVM Constants API
  • 340: One AArch64 Port, Not Two
  • 341: Default CDS Archives
  • 344: Abortable Mixed Collections for G1
  • 346: Promptly Return Unused Committed Memory from G1

There have been, of course, several other blog posts and online resources announcing the JDK 12 release. Some of the most significant are:

Here are some of my previous posts related to JDK 12:

Reinhold completed his "Java 12 / JDK 12: General Availability" message, "Coming up next ... lucky 13!" In a slightly later message on that same mailing list, Reinhold provided the "Proposed schedule for JDK 13":

2019/06/13Rampdown Phase One
2019/07/18Rampdown Phase Two
2019/08/08Initial Release Candidate
2019/08/22Final Release Candidate
2019/09/17General Availability

Monday, January 28, 2019

JDK 13: What AggressiveOpts?

The Java VM flag -XX:+AggressiveOpts was deprecated in JDK 11 [see JDK-8199777 and JDK-8199778] "because its behavior is ill-defined." The "Problem" section of JDK-8199778 further explains (I added the emphasis):

AggressiveOpts has been used as a catch-all method of enabling various experimental performance features, mostly targeted to improve score on very specific benchmarks. Most things it affected has been removed or integrated over time, leaving the behavior of the flag ill-defined and prone to cause more issues than it'll solve. The only effect that the flag currently has is setting AutoBoxCacheMax = 20000 and BiasedLockingStartupDelay = 500. Both can be done manually by setting the corresponding flags on the command line.

According to the document "Java HotSpot VM Options," the -XX:+AggressiveOpts flag was added with J2SE 5 Update 6 to "turn on point performance compiler optimizations that are expected to be default in upcoming releases."

The article "Java's -XX:+AggressiveOpts: Can it slow you down?" examines the -XX:+AggressiveOpts VM flag in detail and looks at some benchmark comparisons. The article concludes, "By retaining legacy flags you make it less likely to get the benefits of newer, faster features in released JVMs."

A much older Kirk Pepperdine article "Poorly chosen Java HotSpot Garbage Collection Flags and how to fix them!" specifically calls out -XX:+AggressiveOpts as an example of a VM flag whose behavior is unknown. Pepperdine writes that recommendations for use of this flag have not changed since Java SE 5.

When the -XX:+AggressiveOpts flag is passed to the JDK 11 Java launcher, a warning is presented: "VM warning: Option AggressiveOpts was deprecated in version 11.0 and will likely be removed in a future release."

In JDK 12, -XX:+AggressiveOpts has been removed as advertised (JDK-8150552) and a warning was presented to anyone trying to use it in conjunction with the Java launcher. The next screen snapshot displays this warning message that states, "VM warning: Ignoring option AggressiveOpts; support was removed in 12.0" (from JDK 12 Early Access Build #29 [2019/1/24]).

In JDK 13 Early Access builds, the VM won't start if -XX:+AggressiveOpts is specified. This is shown in the next screen snapshot (JDK 13 Early Access Build #5 [2019/1/24]).

As the previous image shows, the VM fails to start in JDK 13 when the -XX:+AggressiveOpts flag is specified and it reports the error message, "Unrecognized VM option 'AggressiveOpts'."

The -XX:+AggressiveOpts flag was deprecated in JDK 11, is removed but only shows a warning when specified in JDK 12, and is removed and prevents the VM from starting when specified in JDK 13.

Monday, January 7, 2019

The JDK 13 Train Has Left the Station

JDK 12 [Java SE 12 Platform (JSR 386)] is still in Rampdown Phase 1, but initial work on JDK 13 [Java SE 13 Platform (JSR 388)] has already begun. Draft 26 of the Java SE 12 specification was announced approximately 10 hours before Draft 2 of the Java SE 13 specification was announced.

There are already early access builds of JDK 13 available for Linux, macOS, Windows, and Alpine Linux. As of this writing, the current JDK 13 early access build is #2 (3 January 2019). The JDK 13 Early-Access Release Notes do not yet contain anything of significance.

There are ten change sets associated with Build 2 and another ten change sets associated with JDK 13 Early Access Build 1. There are 33 issues addressed with that build (688 total bugs associated with JDK 13 for "build fix" as this writing).

The temporary home of the Javadoc-based API documentation for JDK 13 is also available at https://download.java.net/java/early_access/jdk13/docs/api/.

The OpenJDK JDK 13 project page describes the project's status: "The development repositories are open for bug fixes, small enhancements, and JEPs as proposed and tracked via the JEP Process."