Showing posts with label java 17. Show all posts
Showing posts with label java 17. Show all posts

Wednesday, March 31, 2021

Chasing Java's release train, from 8 to 16. Part 1: JDK 9, the last big banger ...

A lot has been said about JDK's new 6 months release cadence cycle, and arguably, this is one of the most profound decisions being made in the recent history of the language and JVM platform in general. But if you, like me and other ~60% of the Java developers out there, stuck with JDK 8, the new releases mean little to your day to day job. But the language changes, the standard library changes, the JVM changes and so does the tooling around it. More importantly, the ecosystem also changes by aggressively raising the compatibility baselines. Java in 2021 is not the same as it was in 2020, 2017 and all the more in 2014, when Java 8 has seen the light. The soon to be out JDK 17, the next LTS release, would raise the bar even higher.

So the challenge many of us, seasoned Java developers, are facing is how to stay up to date? The risk of your skills becoming outdated is real. Hence this series of the blog posts: to consolidate the changes in the language, JVM, standard library and tooling over each release, primary from the perspective of the developer. At the end, we should be all set to meet the JDK 17 fully armed. To mention upfront, we are not going to talk about incubating or preview features, nor about come-and-go ones (looking at you, jaotc). Whenever it makes sense, the feature would have a corresponding JEP link, but overall there are many changes not covered by dedicated proposals. I will try to include as much as possible but there are chances some useful features may still slip away.

With that, let us open the stage with JDK 8.

JDK 8

Unsurprisingly, JDK 8 does not only receive the security patches and bugfixes but gets some of the new features from the upstream. The two notable ones are:
  • Improved Docker container detection and resource configuration usage: since 8u191, the JVM is fully container-aware (see please JDK-8146115). The support is only available on Linux based platforms and enabled by default.

  • JFR backport: since 8u262, JFR is supported seamlessly (see please JDK-8223147), no need to pass -XX:+UnlockCommercialFeatures and alike anymore

Although not strictly related, thanks to the JFR backport, the latest JDK Mission Control 8 release comes very handy to everyone. Hopefully, your organization does update to the recent JDK patch releases on the regular basis and these features are already available to you.

JDK 9

The JDK 9 release was just huge (90+ JEPs): not only from the perspective of the bundled features, but also from the impact on the platform and ecosystem. It was 2017 but even today the groundwork established by JDK 9 still continues. Let us take a look at the major features which went into it.

The JDK 9 changeset was massive, at the same time - this is the last big bang release. We have covered most interesting pieces of it, at least from the developer's point of view. In the upcoming posts we are going to dissect other releases, one by one, and reveal the hidden gems of each.

Wednesday, January 27, 2021

JEP-396 and You: strong encapsulation of the JDK internals is the default

Since the inception of the Project Jigsaw, one of its goals was to encapsulate most of the JDK internal APIs in order to give the contributors a freedom to move Java forward at faster pace. JEP-260, delivered along JDK 9 release was a first step in this direction. Indeed, the famous WARNING messages like the ones below

...
WARNING: An illegal reflective access operation has occurred
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release
...

started to appear all over the place. Intended to give the developers and maintainers the time to switch to the alternative, publicly available APIs, they rather provoked the opposite: most just got used to them. Well, if nothing breaks, why bother?

But ... the things are going to change very soon. I think many of you have seen the code which tries to do clever things by gaining the access to the private method or fields of the classes from standard library. One of the notable examples I have seen often enough is overcoming ThreadPoolExecutor's core pool size / maximum pool size semantics (if curious, please read the documentations and complementaty material) by invoking its internal addWorker method. The example below is a simplified illustration of this idea (please, do not do that, ever).

final ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 10, 0L, TimeUnit.MILLISECONDS,
    new LinkedBlockingQueue>Runnable<());

final Runnable task = ...
executor.submit(task);
    
// Queue is empty, enough workers have been created
if (executor.getQueue().isEmpty()) {
    return;
}
    
// Still have some room to go
if (executor.getActiveCount() < executor.getCorePoolSize()) {
    return;
}
    
// Core pool is full but not maxed out, let us add more workers
if (executor.getActiveCount() < executor.getMaximumPoolSize()) {
    final Method addWorker = ThreadPoolExecutor.class.getDeclaredMethod(
        "addWorker", Runnable.class, Boolean.TYPE);
    addWorker.setAccessible(true);
    addWorker.invoke(executor, null, Boolean.FALSE);
}

To be fair, this code works now on JDK 8, JDK 11 or JDK 15: find a private method, make it accessible, good to go. But it will not going to work smoothly in soon to be out JDK 16 and onwards, generating the InaccessibleObjectException exception at runtime upon setAccessible invocation.

Exception in thread "main" java.lang.reflect.InaccessibleObjectException: Unable to make private boolean java.util.concurrent.ThreadPoolExecutor.addWorker(java.lang.Runnable,boolean) accessible: module java.base does not "opens java.util.concurrent" to unnamed module @72ea2f77
        at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:357)
        at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:297)
        at java.base/java.lang.reflect.Method.checkCanSetAccessible(Method.java:199)
        at java.base/java.lang.reflect.Method.setAccessible(Method.java:193)
        ...

So what is happening here? The new JEP-396 continues the endeavours of JEP-260 by strongly encapsulating JDK internals by default. It has been integrated into JDK 16 and JDK 17 early builds which essentially means, no abusive access is going to be allowed anymore. Arguably, this is the right move, though it is very likely to be a disruptive one.

Should you be worrying? It is a good question: if you do not use any internal JDK APIs directly, it is very likely one of the libraries you depend upon may not play by the rules (or may not be ready for rule changes). Hopefully by the time JDK 16 is released, the ecosystem will be in a good shape. There is never good time, we were warned for years and the next milestone is about to be reached. If you could help your favorite library or framework, please do.

The complete list of the exported packages that will no longer be open by default was conveniently made available here, a couple to pay attention to:

java.beans.*
java.io
java.lang.*
java.math
java.net
java.nio.*
java.rmi.*
java.security.*
java.sql
java.text.*
java.time.*
java.util.*

Last but not least, you still could overturn the defaults using the --add-opens command line options, but please use it with great caution (or better, do not use it at all):

$ java --add-opens java.base/java.util.concurrent=ALL-UNNAMED --add-opens java.base/java.net=ALL-UNNAMED ...

Please be proactive and test with latest JDKs in advance, luckily the early access builds (JDK 16, JDK 17) are promptly available to everyone.