articles/2023/March/22-jep442-FFM-Third-Preview at jep442 · minborg/articles · G...
source link: https://github.com/minborg/articles/tree/jep442/2023/March/22-jep442-FFM-Third-Preview
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
JEP 442: Foreign Function & Memory API (Third Preview)
Introduction
The JEP 442 Foreign Function & Memory API (Third Preview) just broke cover. In this article we will explore some new features proposed in this JEP, the JEP being a candidate to be included in JDK 21.
First Reactions
The JEP is a Preview Feature meaning the Foreign Function & Memory API (hereafter "FFM") will not be a final feature in JDK 21. I think some of us hoped it could make it into finality in 21. However, my personal opinion is that the API is very unlikely to change in any significant way and what we are seeing in 21 will be very close to the final FMM API. My bets are the FFM is going to be final in 22 or in 23 at the latest.
What’s New?
The evolution of the FFM API is driven by several factors whereof community feedback is perhaps the most important one. If you are less interested in the FFM history and evolution, please feel free to skip directly to the section Show Me the Code.
Community Feedback
There was a lot of feedback from the previous FFM previews that were taken into consideration in the JEP, some of which are listed here:
-
There were two different ways to allocate native memory (e.g. via
MemorySegment::allocateNative
andArena::alocate
). -
Users are inclined to use
Arena
directly and so, using a derivedSegmentScope
for the lifecycle is a bit confusing. -
There is no default linker for unsupported platforms.
-
There is no "performance mode" for very short-lived native calls.
Improvements
-
Allocation of native memory is now exclusively done via the
Arena::allocate
overloads. The rationale behind this is further described in this post. -
SegmentScope
was dropped in favor of a much smaller construct namedMemorySegment.Scope
which is a pure lifetime abstraction.Arena
is used directly in most cases. -
A default linker based on libffi was added greatly simplifying porting of Java to various platforms.
-
A new linker option was added to mark calls as "trivial" speeding up native calls. A "trivial" method must complete in a duration comparable to a no-op call and must not call back to Java.
NoteThe entire list of improvements can be viewed here
Show Me the Code!
In this chapter, we will explore some of the basic features of the FFM API. The examples below will run only when this PR is merged into the Java mainline repo and only in JDK 21.
Allocate Native Memory
Here is an example of how to allocate 16 bytes of native memory that is automatically managed by the garbage collector (GC).
{
// ...
MemorySegment segment = Arena.ofAuto().allocate(16);
// ...
} // Segment eligible for collection by the GC here.
// Actual time of collection is unspecified.
Allocate Native Memory Deterministically
Here is another example where memory is implicitly and deterministically released:
try (var arena = Arena.ofConfined()) {
var segment = arena.allocate(16);
} // Segment is deterministically freed here
Work with Memory Layouts
Memory layouts can be used to describe the layout of a MemorySegment
. Here is how a point with int
coordinates can be defined:
import static java.lang.foreign.MemoryLayout.PathElement.*;
import static java.lang.foreign.MemoryLayout.*;
import static java.lang.foreign.ValueLayout.*;
private static final MemoryLayout POINT = structLayout(
JAVA_INT.withName("x"),
JAVA_INT.withName("y")
).withName("point");
// Accessor for x
private static final VarHandle X = POINT.varHandle(groupElement("x"));
// Accessor for y
private static final VarHandle Y = POINT.varHandle(groupElement("y"));
Armed with these static variables, we can (somewhat manually) roll a memory-segment-backed point:
try (var arena = Arena.ofConfined()) {
MemorySegment point = arena.allocate(POINT);
X.set(point, 3);
Y.set(point, 4);
System.out.println(
Arrays.toString(point.toArray(JAVA_INT))
);
} // Point is deterministically freed here
When run, this program will produce the following output:
[3, 4]
Encapsulating Memory Layouts
It is often better to encapsulate the inner workings of constructs that are using memory layouts. Here is how a custom Point
class can be written using a backing native MemorySegment
:
static final class Point {
private static final MemoryLayout POINT = structLayout(
JAVA_INT.withName("x"),
JAVA_INT.withName("y")
).withName("point");
private static final VarHandle X = POINT.varHandle(groupElement("x"));
private static final VarHandle Y = POINT.varHandle(groupElement("y"));
private final MemorySegment segment;
public Point(Arena arena) {
this.segment = arena.allocate(POINT);
}
int x() {
return (int) X.get(segment);
}
int y() {
return (int) Y.get(segment);
}
void x(int x) {
X.set(segment, x);
}
void y(int y) {
Y.set(segment, y);
}
@Override
public String toString() {
return "(" + x() + ", " + y() + ")";
}
@Override
public boolean equals(Object o) {
return o instanceof Point that &&
this.x() == that.x() &&
this.x() == that.y();
}
@Override
public int hashCode() {
return Objects.hash(x(), y());
}
}
Note that we are passing an Arena
to the constructor so that we can control the lifecycle of the MemorySegment
used. Here is how the Point class can be used:
try (var arena = Arena.ofConfined()) {
var point = new Point(arena);
point.x(3);
point.y(4);
System.out.println(point);
} // Point is deterministically freed here
When run, this program will produce the following output:
(3, 4)
Call a Native Method
With FFM, it is possible to call many native functions directly. Below is an example where we invoke the system library call 'strlen' directly from Java. This is made in two steps where, in step one, we obtain a MethodHandle
for the native method:
Linker linker = Linker.nativeLinker();
MethodHandle strlen = linker.downcallHandle(
linker.defaultLookup().find("strlen").get(),
FunctionDescriptor.of(JAVA_LONG, ADDRESS)
);
With the MethodHandle
strlen
, we can, in a second step, easily invoke the method directly from Java:
try (Arena arena = Arena.ofConfined()) {
MemorySegment str = arena.allocateUtf8String("Hello");
long len = (long) strlen.invoke(str);
System.out.println("The length is " + len);
}
When run, this program will produce the following output:
The length is 5
This is correct as "Hello" consists of five ASCII characters (not including the terminating '/0' character used by C/C++).
Clone Me!
All code and the entire presentation can be cloned via https://github.com/minborg/articles
Safe Harbor Statement
The following is intended to outline our general product direction. It is intended for information purposes only, and may not be incorporated into any contract. It is not a commitment to deliver any material, code, or functionality, and should not be relied upon in making purchasing decisions. The development, release, timing, and pricing of any features or functionality described for Oracle’s products may change and remains at the sole discretion of Oracle Corporation.
Recommend
-
31
While COVID-19 is dominating headlines across the world, it’s important to note that in the world of machine learning, many companies...
-
4
Why General Inheritance is Flawed and How to Finally Fix itBy leveraging composition and the final keyword in the right way, you can improve your programming skills and become a better Java programmer. General inheritance, wh...
-
0
Announcing Third Room Tech Preview 12022-09-27 — Releases — Matthew HodgsonWe're excited to announce the first tech preview of
-
1
This is an opinion editorial by Stephan Livera, host of the “Stephan Livera Podcast” and managing director of Swan Bitcoin International.As the 2022 chapter closes, it’s time to turn our eyes to what’s coming in 2023. Here are some...
-
7
Foreign Function and Memory API by Per Minborg
-
7
Vue.js Amsterdam 2023 – A Recap of the Two-Day Event Vue.js Amsterdam 2023 was an exciting two-day technology conference that took place on 9th and 10th February 2023 in Amsterdam, Netherlands. The event brought together creators, exper...
-
5
OwnerMaurizio CimadamoreTypeFeatureScopeSEStatusCandidateComponentcore-libsDiscussion
-
6
Nov 7, 2023 Earnings eBay Inc. Reports Third Quarter 2023 Results Press Release ...
-
4
49 UX Metrics, Methods, & Measurement Articles from 2023 Jim Lewis, PhD • Jeff Sauro, PhD ...
-
4
BMW to launch third all-electric i5 variant in March as a dual motor 2025 model February 1, 2024...
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK