Summary
This CSR refers to the third iteration of the Foreign Memory Access (an incubating Java API) originally targeted for Java 14 (and later re-incubated in 15), with the goal of refining some of the API rough edges, as well as addressing the feedback received so far from developers.
Problem
Real-world use of the Foreign Memory Access API revealed some remaining usability issues, listed below:
- The API's bias towards confinement is still too prominent, despite the addition (in 15) of an API point to cooperatively change thread ownership of a given segment (serial confinement); the main issue is that, even with the new API, it is still not possible to close a memory segment from a thread other than the owner thread, and that makes memory segment hard to use with certain API idioms
- While deterministic deallocation is one of the core foundations of the Foreign Memory Access API, users have expressed interest in having the ability to optionally register segments against a
Cleaner
instance, so that segment deallocation could be guaranteed regardless of whether an explicit call toMemorySegment::close
is made - The distinction between checked and unchecked memory addresses is too subtle. Checked addresses have a reference to the owning segment, and can be safely dereferenced, whereas unchecked addresses can't. This leads to verbosity in clients attempting to dereference a
MemoryAddress
instance, as the client has first to query if a segment is available, and then act accordingly. - Forcing developers to exclusively use
VarHandle
when it comes to dereference memory is a bit too much; there are cases where the expressive power of var handles come in handy (e.g. in concurrent contexts, when fencing is required, or when structural access is needed), but in simpler cases it would be nice to have a ready-made collections of dereference API points which can be used in a type-safe way. - While a memory address and a memory segment generally represent separate concepts, it is possible to go from the latter to the former (e.g. by calling the
MemorySegment::baseAddress
method). Our experiments with thejextract
tool revealed that the need for this explicit conversion has often a pretty big impact in the verbosity of clients interacting with the native bindings generated by jextract.
Solution
Here we describe the main ideas behind the API changes brought forward in this CSR:
- First, the notion of shared memory segment is introduced. A shared memory segment is created by invoking the
MemorySegment::share
method that returns a new memory segment instance, which is backed by the same memory region, and has no owner thread. This means that the segment will be effectively accessible (and closeable) from multiple threads (possibly in a concurrent fashion). The safety promises of the Foreign Memory Access API are preserved by a sophisticated lock-free synchronization scheme which relies on thread-local handshakes (see JEP 312). - A new method is added to memory segments,
MemorySegment::registerCleaner(Cleaner)
that returns a new segment instance, which is backed by the same memory region, and supports implicit deallocation. - Memory access var handles now use memory segments as the basic dereference unit. That is, the most basic memory access var handle accepts two coordinates: the segment to be dereferenced and the offset within the segment at which the dereference should occur. All other, structural memory access var handles can be composed from this basic one. As a result,
MemoryAddress
has reverted back to being a dumb carrier for an object/offset unsafe addressing coordinate pair. That is, there is no way to go from an address back to a segment (except unsafely viaMemoryAddress::asSegmentRestricted
but that creates a new segment). - A new class, namely
MemoryAccess
is introduced. This class contains several dereference methods to get and set values in memory, with different Java carriers. There are different access modes supported: a basic one which simply takes a memory segment and dereferences it at its base address; other, more sophisticated, modes allows a byte offset or a logical index to be passed as well, to support common array-like access idioms. - To better capture the commonality between a memory segment and a memory address a new interface, namely
Addressable
has been introduced. This interface describes entities that can be mapped into a memory address. This interface is implemented, trivially, byMemoryAddress
and also byMemorySegment
(a memory segment can always be projected to its base address). We plan, with subsequent JEPs (e.g. JEP 389) to add more implementations of this interface. - The hierarchy of interface
MappedMemorySegment <: MemorySegment
poses issues when it comes toVarHandle
invocations; since now the memory access var handle take aMemorySegment
access coordinate, calling such var handles with aMappedMemorySegment
coordinate will result in a non-exact var handle call, thus greatly degrading performances; we have decided to drop theMappedMemorySegment
interface and instead introduce an helper class, namelyMappedMemorySegments
, which contains static methods which provide the same functionality as what was provided byMappedMemorySegment
.
Specification
Here are some useful links which should help in navigating through the changes in the API.
Javadoc:
http://cr.openjdk.java.net/~mcimadamore/8254162_v5/javadoc
Specdiff:
http://cr.openjdk.java.net/~mcimadamore/8254162_v5/specdiff_out
Pull request:
https://github.com/openjdk/jdk/pull/548
In addition, a specdiff of the changes as of November 9th 2020 has been attached to this CSR.
- csr of
-
JDK-8254162 Implementation of Foreign-Memory Access API (Third Incubator)
- Resolved