In this article, you will see the goals of each of these projects, their current state (as of January 2022), and their short-term plans.
Project Valhalla
Project Valhalla has two goals: an obvious one and a subtle one. The obvious goal is to introduce a new kind of type that “codes like a class, works like an int,” as the mission statement says. The subtle goal is to heal the rift in Java’s type system that separates reference types from primitive types; this will allow generification over all types.
Yes, you’ll be able to create an ArrayList<int> that’s backed by an int[], with no boxing needed.
Valhalla launched in 2014 right after Java 8 was released and has spent much of that time in an exploratory phase during which the people behind the project, led by Brian Goetz, experimented with various proof-of-concept implementations. Over the last year, these efforts have crystalized into a set of concrete proposals.
Current state. The current plan is to introduce two new kinds of types: value classes and primitive classes.
Value classes disavow identity and thus are shallowly immutable (meaning all fields are final). These classes can still be null and have the same safety guarantees as regular classes.
// has no identity
// has references (can be null)
value class Circle implements Shape {
// implicitly final
private double radius;
// [... constructor ...]
@Override
public double area() {
return Math.PI * radius * radius;
}
}
Primitive classes take this concept one step further: In addition to giving up identity, they also give up references. That means they can’t be null, and thus primitive classes need a reasonable default value. Primitive classes can tear under concurrent assignments just like double and long can on some JVMs.
// has no identity
// no references (no null, but tearing)
primitive class Circle implements Shape {
// implicitly final
private double radius;
// [... constructor ...]
@Override
public double area() {
return Math.PI * radius * radius;
}
}
Value classes give the JVM more options to improve performance because the JVM doesn’t have to track their identity. Because value classes can be more optimized with the JVM, they are as performant as today’s primitives.
With Project Valhalla, generics will be made universal, meaning the language will allow all these types as parameters (that’s ArrayList<int>). Generics will also allow specialization, meaning they’ll rely on primitive classes instead of boxes (which brings you the backing int[]).
You can learn more from the excellent Valhalla documentation, which was significantly updated in early January 2022.
The future. The plan is to roll out Valhalla in three phases.
◉ Value objects, for which a JEP draft exists
◉ Primitive classes, for which there are JEP 401 (primitive classes) and JEP 402 (classes for the basic primitives), as well as universal generics, which also has a draft JEP
◉ Specialized generics, for which there is not yet a draft JEP
Project Panama
Much like the Panama Canal connects the Pacific Ocean and the Atlantic Ocean, Project Panama wants to connect the JVM with foreign (meaning non-Java) libraries—or rather improve and enrich that connection. The project is currently led by Maurizio Cimadamore and started in 2014. After a few years of exploration, Panama arrived at the following goals for interacting with foreign code:
◉ Provide an alternative to the ByteBuffer API with similar performance and safety characteristics, but without some of the downsides.
◉ Make native libraries more accessible by replacing the Java Native Interface (JNI) with a more Java-oriented workflow.
◉ Provide basic building blocks that enable other frameworks to define higher-level native interoperability.
Also in Panama is an exploration of better interaction with CPU vector instructions.
Current state. On the vector front, things are far along. The Vector API began incubating in JDK 16 and is still incubating in JDK 18 as JEP 417. The API lets you write Java code that the just-in-time compiler can reliably compile down to vector instructions optimized for the executing CPU.
// vector API demonstration; computing c = -(a*a + b*b)
void vectorComputation(float[] a, float[] b, float[] c) {
for (int i = 0; i < a.length; i += SPECIES.length()) {
var m = SPECIES.indexInRange(i, a.length);
var va = FloatVector.fromArray(SPECIES, a, i, m);
var vb = FloatVector.fromArray(SPECIES, b, i, m);
var vc = va.mul(va).add(vb.mul(vb)).neg();
vc.intoArray(c, i, m);
}
}
The interaction with native code is also making good progress. JEP 419 (Foreign Function and Memory API), which is part of JDK 18, is the second incubation of two APIs and the command-line tool jextract. Together they achieve the three goals stated earlier; several projects, from Netty and Lucene to OpenGL and OpenSSL, have already been tested on it.
// foreign memory API demonstration
try (ResourceScope scope = ResourceScope
.newConfinedScope()) {
MemorySegment s1 = MemorySegment
.map(path, 0, 100000, READ_WRITE, scope);
MemorySegment s2 = MemorySegment
.allocateNative(100, scope);
// use the segments here
} // segments released here
The future. In 2022 the foreign memory and access APIs will start the transition from being incubating APIs in the temporary jdk.incubator.foreign module to becoming preview APIs in the java.base module. There are also still a few improvements to be made under the hood. For example, the jextract tool may be spun out into its own project with its own repository, delivery mechanism, and cadence, so it’s easier to contribute to and can be improved and released more rapidly.
The Vector API is special in the regard that it would look a bit different if value and primitive types were already available. Because differences can’t be introduced without introducing incompatible changes, the Vector API will remain incubating until the necessary Valhalla features are implemented.
Project Loom
Project Loom takes a two-pronged approach toward improving Java’s concurrency model.
◉ Introduce threads that are lightweight (meaning you should be able to have millions of them) and user-mode (meaning managed by the JVM, not by the operating system).
◉ Introduce new concurrency programming models that make use of those lightweight threads.
The goal is to improve the performance of heavily asynchronous code. To borrow language from Project Valhalla, you could say “codes like sync, works like async.”
Loom is led by Ron Pressler, who launched the effort in 2018. From the get-go, the team had a pretty good idea of where things were going; they spent a lot of time verifying the idea and refactoring APIs to remove roadblocks.
Examples of refactored APIs are JEP 353, which reimplemented the legacy Socket API in JDK 15, and JEP 418, which added an internet host-name resolution-service provider interface to JDK 18.
Current state. In recent months, two fundamental JEPs were drafted. The first introduces virtual threads, the lightweight, user-mode threads that Loom envisioned.
The other draft proposes a structured concurrency API. The proposed API realizes structured concurrency by offering a closable StructuredExecutor that lets you spawn a virtual thread for each concurrent task, as well as various completion handlers that let you choose common behaviors such as shutting down running tasks as soon as one of them succeeds or fails.
The idea is to create the executor and handler, launch all tasks, wait for their completion, and handle errors or process results before shutting the executor down—implicitly if they are used in a try-with-resources block. And all of it is accomplished with just a few of lines of code!
String doTwoThingsConcurrently()
throws IOException, InterruptedException {
try (var executor = StructuredExecutor.open()) {
var handler =
new StructuredExecutor.ShutdownOnFailure();
Future<String> one = executor
.fork(() -> doOneThing(), handler);
Future<String> two = executor
.fork(() -> doAnotherThing(), handler);
executor.join();
handler.throwIfFailed();
return one.resultNow() + two.resultNow();
} catch (ExecutionException ex) {
if (ex.getCause() instanceof IOException ioe)
throw ioe;
throw new RuntimeException(ex);
}
}
Project Loom’s early-access build (built on an incomplete build of JDK 19) already contains these proposals. Give it a try; it’s really cool!
The future. The next steps for Project Loom are to gather more feedback on the two draft JEPs before finalizing them and eventually merging these features as previews into the JDK main development line. Aside from refinements of these features during the preview phase, there’s still more work ahead for Loom.
For one, with millions of threads, Java’s classic thread views, heap dumps, and other analysis systems become nearly unreadable. There’s a lot that needs to be done there to make highly concurrent apps easier to debug.
There is also more work planned on the structured concurrency front; this concept is far from fully figured out, and there’ll surely be more experiments with new APIs and API building blocks.
Project Amber
Project Amber explores smaller, productivity-oriented Java language features. Some of them are part of a larger story, but many are independent of one another.
Led by Brian Goetz, Amber launched in 2017. Due to its focus on smaller features, Amber has already delivered several of them.
◉ Local-variable type inference, or var for short, in Java 10
◉ Text blocks in Java 15
◉ Records in Java 16
◉ Parts of a larger pattern matching story with switch improvements such as arrow syntax and use as expressions in Java 14, type patterns in Java 16, and sealed classes in Java 17
Current state. Amber is delivering pattern-matching builds on three pillars—some delivered as a final offering and others in preview. First came the improvements of switch, in this context most importantly checking exhaustiveness.
int value = switch(myBoolEnum) {
case FALSE -> 0;
case TRUE -> 1;
};
Amber introduced patterns themselves, initially offering only type patterns.
double area;
if (shape instanceof Rectangle r)
area = r.height() * r.width();
It also brought developers the concept of sealed classes, which enables exhaustiveness checks when switching over types.
sealed interface Shape
permits Circle, Rectangle {
}
The beam that rests on and connects these three pillars is the inclusion of patterns in switch. In JDK 17, this feature has seen its first preview, and there will be a second one in JDK 18 in JEP 420.
double area = switch (shape) {
case Rectangle r ->
r.height() * r.width();
case Circle c ->
Math.PI * square(c.radius());
}
The future. With all pattern matching basics potentially finalized in JDK 19, you can expect more features and patterns that build on this, for example
◉ Refining patterns with additional conditions and null checks. This is also in JEP 420.
◉ Deconstruction patterns for records and arrays. After the introduction of explicit deconstructors, perhaps you will see deconstructors for regular classes.
◉ Perhaps you will see custom patterns, for example, to implement one that combines a Map class’s containsKey and get methods, so that if the pattern applies to a map, meaning if it contains that key, the associated value is assigned to the pattern variable, as shown below.
Map<String, Integer> numbers = // ...;
// totally made-up syntax for the
// hypothetical contains/get pattern
if (numbers contains "one" number)
// use 'Integer number' here...
It’s important to remember that despite its recent JEPs, Amber’s goals are much broader than pattern matching. An interesting idea currently being explored is template strings but with a cool twist. Look at the draft JEP for templated strings and template policies, for example.
Source: oracle.com
0 comments:
Post a Comment