diff --git a/src/java.base/share/classes/java/lang/foreign/Arena.java b/src/java.base/share/classes/java/lang/foreign/Arena.java index 457847d76c62f..78828266923f1 100644 --- a/src/java.base/share/classes/java/lang/foreign/Arena.java +++ b/src/java.base/share/classes/java/lang/foreign/Arena.java @@ -43,9 +43,10 @@ * to obtain native segments. *

* The simplest arena is the {@linkplain Arena#global() global arena}. The global arena - * features an unbounded lifetime. As such, native segments allocated with the global arena are always - * accessible and their backing regions of memory are never deallocated. Moreover, memory segments allocated with the - * global arena can be {@linkplain MemorySegment#isAccessibleBy(Thread) accessed} from any thread. + * features an unbounded lifetime. The scope of the global arena is the global scope. + * As such, native segments allocated with the global arena are associated are always accessible and their backing regions + * of memory are never deallocated. + * Moreover, memory segments allocated with the global arena can be {@linkplain MemorySegment#isAccessibleBy(Thread) accessed} from any thread. * {@snippet lang = java: * MemorySegment segment = Arena.global().allocate(100, 1); // @highlight regex='global()' * ... @@ -53,7 +54,8 @@ *} *

* Alternatively, clients can obtain an {@linkplain Arena#ofAuto() automatic arena}, that is an arena - * which features a bounded lifetime that is managed, automatically, by the garbage collector. As such, the regions + * which features a bounded lifetime that is managed, automatically, by the garbage collector. The scope + * of an automatic arena is an automatic scope. As such, the regions * of memory backing memory segments allocated with the automatic arena are deallocated at some unspecified time * after the automatic arena (and all the segments allocated by it) becomes * unreachable, as shown below: @@ -67,11 +69,9 @@ * Rather than leaving deallocation in the hands of the Java runtime, clients will often wish to exercise control over * the timing of deallocation for regions of memory that back memory segments. Two kinds of arenas support this, * namely {@linkplain #ofConfined() confined} and {@linkplain #ofShared() shared} arenas. They both feature - * bounded lifetimes that are managed manually. For instance, the lifetime of a confined arena starts when the confined - * arena is created, and ends when the confined arena is {@linkplain #close() closed}. As a result, the regions of memory - * backing memory segments allocated with a confined arena are deallocated when the confined arena is closed. - * When this happens, all the segments allocated with the confined arena are invalidated, and subsequent access - * operations on these segments will fail {@link IllegalStateException}: + * bounded lifetimes that are managed manually. For instance, when a confined arena is {@linkplain #close() closed} + * successfully, its scope is {@linkplain Scope#isAlive() invalidated}. As a result, all the memory segments allocated + * by the arena can no longer be accessed, and their regions of memory are deallocated: * * {@snippet lang = java: * MemorySegment segment = null; @@ -219,7 +219,7 @@ static Arena ofAuto() { */ static Arena global() { class Holder { - static final Arena GLOBAL = MemorySessionImpl.GLOBAL.asArena(); + static final Arena GLOBAL = MemorySessionImpl.GLOBAL_SESSION.asArena(); } return Holder.GLOBAL; } diff --git a/src/java.base/share/classes/java/lang/foreign/Linker.java b/src/java.base/share/classes/java/lang/foreign/Linker.java index 9adca13b774c4..747e2b9270d29 100644 --- a/src/java.base/share/classes/java/lang/foreign/Linker.java +++ b/src/java.base/share/classes/java/lang/foreign/Linker.java @@ -365,7 +365,7 @@ * * The size of the segment returned by the {@code malloc} downcall method handle is * zero. Moreover, the scope of the - * returned segment is a fresh scope that is always alive. To provide safe access to the segment, we must, + * returned segment is the global scope. To provide safe access to the segment, we must, * unsafely, resize the segment to the desired size (100, in this case). It might also be desirable to * attach the segment to some existing {@linkplain Arena arena}, so that the lifetime of the region of memory * backing the segment can be managed automatically, as for any other native segment created directly from Java code. @@ -563,7 +563,7 @@ static Linker nativeLinker() { *

* Moreover, if the provided function descriptor's return layout is an {@linkplain AddressLayout address layout}, * invoking the returned method handle will return a native segment associated with - * a fresh scope that is always alive. Under normal conditions, the size of the returned segment is {@code 0}. + * the global scope. Under normal conditions, the size of the returned segment is {@code 0}. * However, if the function descriptor's return layout has a {@linkplain AddressLayout#targetLayout() target layout} * {@code T}, then the size of the returned segment is set to {@code T.byteSize()}. *

@@ -602,7 +602,7 @@ static Linker nativeLinker() { * upcall stub segment will be deallocated when the provided confined arena is {@linkplain Arena#close() closed}. *

* An upcall stub argument whose corresponding layout is an {@linkplain AddressLayout address layout} - * is a native segment associated with a fresh scope that is always alive. + * is a native segment associated with the global scope. * Under normal conditions, the size of this segment argument is {@code 0}. * However, if the address layout has a {@linkplain AddressLayout#targetLayout() target layout} {@code T}, then the size of the * segment argument is set to {@code T.byteSize()}. diff --git a/src/java.base/share/classes/java/lang/foreign/MemoryLayout.java b/src/java.base/share/classes/java/lang/foreign/MemoryLayout.java index 635caff677624..a9c58428c7c2e 100644 --- a/src/java.base/share/classes/java/lang/foreign/MemoryLayout.java +++ b/src/java.base/share/classes/java/lang/foreign/MemoryLayout.java @@ -460,7 +460,7 @@ default MethodHandle byteOffsetHandle(PathElement... elements) { * *

* If the selected layout is an {@linkplain AddressLayout address layout}, calling {@link VarHandle#get(Object...)} - * on the returned var handle will return a new memory segment. The segment is associated with a fresh scope that is + * on the returned var handle will return a new memory segment. The segment is associated with a scope that is * always alive. Moreover, the size of the segment depends on whether the address layout has a * {@linkplain AddressLayout#targetLayout() target layout}. More specifically: *

*

@@ -635,7 +635,7 @@ default MemorySegment asSlice(long offset, MemoryLayout layout) { * MemorySegment cleanupSegment = MemorySegment.ofAddress(this.address()) * .reinterpret(byteSize()); * } - * That is, the cleanup action receives a segment that is associated with a fresh scope that is always alive, + * That is, the cleanup action receives a segment that is associated with the global scope, * and is accessible from any thread. The size of the segment accepted by the cleanup action is {@link #byteSize()}. *

* This method is restricted. @@ -674,7 +674,7 @@ default MemorySegment asSlice(long offset, MemoryLayout layout) { * MemorySegment cleanupSegment = MemorySegment.ofAddress(this.address()) * .reinterpret(newSize); * } - * That is, the cleanup action receives a segment that is associated with a fresh scope that is always alive, + * That is, the cleanup action receives a segment that is associated with the global scope, * and is accessible from any thread. The size of the segment accepted by the cleanup action is {@code newSize}. *

* This method is restricted. @@ -1183,11 +1183,9 @@ default void setString(long offset, String str, Charset charset) { *

* If the provided buffer has been obtained by calling {@link #asByteBuffer()} on a memory segment whose * {@linkplain Scope scope} is {@code S}, the returned segment will be associated with the - * same scope {@code S}. Otherwise, the scope of the returned segment is a fresh scope that is always alive. - *

- * The scope associated with the returned segment keeps the provided buffer reachable. As such, if - * the provided buffer is a direct buffer, its backing memory region will not be deallocated as long as the - * returned segment (or any of its slices) are kept reachable. + * same scope {@code S}. Otherwise, the scope of the returned segment is an automatic scope that keeps the provided + * buffer reachable. As such, if the provided buffer is a direct buffer, its backing memory region will not be + * deallocated as long as the returned segment (or any of its slices) are kept reachable. * * @param buffer the buffer instance to be turned into a new memory segment. * @return a memory segment, derived from the given buffer instance. @@ -1202,7 +1200,7 @@ static MemorySegment ofBuffer(Buffer buffer) { /** * Creates a heap segment backed by the on-heap region of memory that holds the given byte array. - * The scope of the returned segment is a fresh scope that is always alive, and keeps the given array reachable. + * The scope of the returned segment is an automatic scope that keeps the given array reachable. * The returned segment is always accessible, from any thread. Its {@link #address()} is set to zero. * * @param byteArray the primitive array backing the heap memory segment. @@ -1214,7 +1212,7 @@ static MemorySegment ofArray(byte[] byteArray) { /** * Creates a heap segment backed by the on-heap region of memory that holds the given char array. - * The scope of the returned segment is a fresh scope that is always alive, and keeps the given array reachable. + * The scope of the returned segment is an automatic scope that keeps the given array reachable. * The returned segment is always accessible, from any thread. Its {@link #address()} is set to zero. * * @param charArray the primitive array backing the heap segment. @@ -1226,7 +1224,7 @@ static MemorySegment ofArray(char[] charArray) { /** * Creates a heap segment backed by the on-heap region of memory that holds the given short array. - * The scope of the returned segment is a fresh scope that is always alive, and keeps the given array reachable. + * The scope of the returned segment is an automatic scope that keeps the given array reachable. * The returned segment is always accessible, from any thread. Its {@link #address()} is set to zero. * * @param shortArray the primitive array backing the heap segment. @@ -1238,7 +1236,7 @@ static MemorySegment ofArray(short[] shortArray) { /** * Creates a heap segment backed by the on-heap region of memory that holds the given int array. - * The scope of the returned segment is a fresh scope that is always alive, and keeps the given array reachable. + * The scope of the returned segment is an automatic scope that keeps the given array reachable. * The returned segment is always accessible, from any thread. Its {@link #address()} is set to zero. * * @param intArray the primitive array backing the heap segment. @@ -1250,7 +1248,7 @@ static MemorySegment ofArray(int[] intArray) { /** * Creates a heap segment backed by the on-heap region of memory that holds the given float array. - * The scope of the returned segment is a fresh scope that is always alive, and keeps the given array reachable. + * The scope of the returned segment is an automatic scope that keeps the given array reachable. * The returned segment is always accessible, from any thread. Its {@link #address()} is set to zero. * * @param floatArray the primitive array backing the heap segment. @@ -1262,7 +1260,7 @@ static MemorySegment ofArray(float[] floatArray) { /** * Creates a heap segment backed by the on-heap region of memory that holds the given long array. - * The scope of the returned segment is a fresh scope that is always alive, and keeps the given array reachable. + * The scope of the returned segment is an automatic scope that keeps the given array reachable. * The returned segment is always accessible, from any thread. Its {@link #address()} is set to zero. * * @param longArray the primitive array backing the heap segment. @@ -1274,7 +1272,7 @@ static MemorySegment ofArray(long[] longArray) { /** * Creates a heap segment backed by the on-heap region of memory that holds the given double array. - * The scope of the returned segment is a fresh scope that is always alive, and keeps the given array reachable. + * The scope of the returned segment is an automatic scope that keeps the given array reachable. * The returned segment is always accessible, from any thread. Its {@link #address()} is set to zero. * * @param doubleArray the primitive array backing the heap segment. @@ -1285,13 +1283,13 @@ static MemorySegment ofArray(double[] doubleArray) { } /** - * A zero-length native segment modelling the {@code NULL} address. + * A zero-length native segment modelling the {@code NULL} address. Equivalent to {@code MemorySegment.ofAddress(0L)}. */ MemorySegment NULL = new NativeMemorySegmentImpl(); /** * Creates a zero-length native segment from the given {@linkplain #address() address value}. - * The returned segment is associated with a scope that is always alive, and is accessible from any thread. + * The returned segment is associated with the global scope, and is accessible from any thread. *

* On 32-bit platforms, the given address value will be normalized such that the * highest-order ("leftmost") 32 bits of the {@link MemorySegment#address() address} @@ -1718,7 +1716,7 @@ default void set(ValueLayout.OfDouble layout, long offset, double value) { /** * Reads an address from this segment at the given offset, with the given layout. The read address is wrapped in - * a native segment, associated with a fresh scope that is always alive. Under normal conditions, + * a native segment, associated with the global scope. Under normal conditions, * the size of the returned segment is {@code 0}. However, if the provided address layout has a * {@linkplain AddressLayout#targetLayout() target layout} {@code T}, then the size of the returned segment * is set to {@code T.byteSize()}. @@ -2157,7 +2155,7 @@ default void setAtIndex(ValueLayout.OfDouble layout, long index, double value) { /** * Reads an address from this segment at the given at the given index, scaled by the given layout size. The read address is wrapped in - * a native segment, associated with a fresh scope that is always alive. Under normal conditions, + * a native segment, associated with the global scope. Under normal conditions, * the size of the returned segment is {@code 0}. However, if the provided address layout has a * {@linkplain AddressLayout#targetLayout() target layout} {@code T}, then the size of the returned segment * is set to {@code T.byteSize()}. @@ -2365,11 +2363,38 @@ static long mismatch(MemorySegment srcSegment, long srcFromOffset, long srcToOff /** * A scope models the lifetime of all the memory segments associated with it. That is, a memory segment - * cannot be accessed if its associated scope is not {@linkplain #isAlive() alive}. A new scope is typically - * obtained indirectly, by creating a new {@linkplain Arena arena}. + * cannot be accessed if its associated scope is not {@linkplain #isAlive() alive}. Scope instances can be compared + * for equality. That is, two scopes are considered {@linkplain #equals(Object)} if they denote the same lifetime. + *

+ * The lifetime of a memory segment can be either unbounded or bounded. An unbounded lifetime + * is modelled with the global scope. The global scope is always {@link #isAlive() alive}. As such, a segment + * associated with the global scope features trivial temporal bounds, and is always accessible. + * Segments associated with the global scope are: + *

*

- * Scope instances can be compared for equality. That is, two scopes - * are considered {@linkplain #equals(Object)} if they denote the same lifetime. + * Conversely, a bounded lifetime is modelled with a segment scope that can be invalidated, either {@link Arena#close() explicitly}, + * or automatically, by the garbage collector. A segment scope that is invalidated automatically is an automatic scope. + * An automatic scope is always {@link #isAlive() alive} as long as it is reachable. + * Segments associated with an automatic scope are: + *

+ * If two memory segments are obtained from the same {@linkplain #ofBuffer(Buffer) buffer} + * or {@linkplain #ofArray(int[]) array}, the automatic scopes associated with said segments are considered + * {@linkplain #equals(Object) equal}, as the two segments have the same lifetime: + * {@snippet lang=java : + * byte[] arr = new byte[10]; + * MemorySegment segment1 = MemorySegment.ofArray(arr); + * MemorySegment segment2 = MemorySegment.ofArray(arr); + * assert segment1.scope().equals(segment2.scope()); + * } */ sealed interface Scope permits MemorySessionImpl { /** diff --git a/src/java.base/share/classes/java/lang/foreign/SymbolLookup.java b/src/java.base/share/classes/java/lang/foreign/SymbolLookup.java index aafe44b4d4092..632543c995782 100644 --- a/src/java.base/share/classes/java/lang/foreign/SymbolLookup.java +++ b/src/java.base/share/classes/java/lang/foreign/SymbolLookup.java @@ -165,7 +165,7 @@ default SymbolLookup or(SymbolLookup other) { *

* Libraries associated with a class loader are unloaded when the class loader becomes * unreachable. The symbol lookup - * returned by this method is associated with a fresh {@linkplain MemorySegment.Scope scope} which keeps the caller's + * returned by this method is associated with an automatic {@linkplain MemorySegment.Scope scope} which keeps the caller's * class loader reachable. Therefore, libraries associated with the caller's class loader are kept loaded * (and their symbols available) as long as a loader lookup for that class loader, or any of the segments * obtained by it, is reachable. @@ -189,7 +189,7 @@ static SymbolLookup loaderLookup() { if ((loader == null || loader instanceof BuiltinClassLoader)) { loaderArena = Arena.global(); } else { - MemorySessionImpl session = MemorySessionImpl.heapSession(loader); + MemorySessionImpl session = MemorySessionImpl.createHeap(loader); loaderArena = session.asArena(); } return name -> { diff --git a/src/java.base/share/classes/jdk/internal/foreign/AbstractMemorySegmentImpl.java b/src/java.base/share/classes/jdk/internal/foreign/AbstractMemorySegmentImpl.java index 6e84f7f4b9ea3..4dc9f5d0c7a08 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/AbstractMemorySegmentImpl.java +++ b/src/java.base/share/classes/jdk/internal/foreign/AbstractMemorySegmentImpl.java @@ -54,6 +54,7 @@ import jdk.internal.util.ArraysSupport; import jdk.internal.util.Preconditions; import jdk.internal.vm.annotation.ForceInline; +import sun.nio.ch.DirectBuffer; import static java.lang.foreign.ValueLayout.JAVA_BYTE; @@ -537,7 +538,7 @@ public static AbstractMemorySegmentImpl ofBuffer(Buffer bb) { if (bufferSegment != null) { bufferScope = bufferSegment.scope; } else { - bufferScope = MemorySessionImpl.heapSession(bb); + bufferScope = MemorySessionImpl.createHeap(bufferRef(bb)); } if (base != null) { if (base instanceof byte[]) { @@ -565,6 +566,17 @@ public static AbstractMemorySegmentImpl ofBuffer(Buffer bb) { } } + private static Object bufferRef(Buffer buffer) { + if (buffer instanceof DirectBuffer directBuffer) { + // direct buffer, return either the buffer attachment (for slices and views), or the buffer itself + return directBuffer.attachment() != null ? + directBuffer.attachment() : directBuffer; + } else { + // heap buffer, return the underlying array + return NIO_ACCESS.getBufferBase(buffer); + } + } + private static int getScaleFactor(Buffer buffer) { if (buffer instanceof ByteBuffer) { return 0; diff --git a/src/java.base/share/classes/jdk/internal/foreign/GlobalSession.java b/src/java.base/share/classes/jdk/internal/foreign/GlobalSession.java index 2d78a66eb6f8d..c075778b5ee1d 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/GlobalSession.java +++ b/src/java.base/share/classes/jdk/internal/foreign/GlobalSession.java @@ -25,20 +25,23 @@ package jdk.internal.foreign; +import jdk.internal.access.JavaNioAccess; +import jdk.internal.access.SharedSecrets; import jdk.internal.vm.annotation.ForceInline; +import sun.nio.ch.DirectBuffer; + +import java.nio.Buffer; +import java.util.Objects; /** * The global, non-closeable, shared session. Similar to a shared session, but its {@link #close()} method throws unconditionally. * Adding new resources to the global session, does nothing: as the session can never become not-alive, there is nothing to track. * Acquiring and or releasing a memory session similarly does nothing. */ -final class GlobalSession extends MemorySessionImpl { - - final Object ref; +non-sealed class GlobalSession extends MemorySessionImpl { - public GlobalSession(Object ref) { + public GlobalSession() { super(null, null); - this.ref = ref; } @Override @@ -67,4 +70,32 @@ void addInternal(ResourceList.ResourceCleanup resource) { public void justClose() { throw nonCloseable(); } + + /** + * This is a global session that wraps a heap object. Possible objects are: Java arrays, buffers and + * class loaders. Objects of two heap sessions are compared by identity. That is, if the wrapped object is the same, + * then the resulting heap sessions are also considered equals. We do not compare the objects using + * {@link Object#equals(Object)}, as that would be problematic when comparing buffers, whose equality and + * hash codes are content-dependent. + */ + static class HeapSession extends GlobalSession { + + final Object ref; + + public HeapSession(Object ref) { + super(); + this.ref = Objects.requireNonNull(ref); + } + + @Override + public boolean equals(Object obj) { + return obj instanceof HeapSession session && + ref == session.ref; + } + + @Override + public int hashCode() { + return System.identityHashCode(ref); + } + } } diff --git a/src/java.base/share/classes/jdk/internal/foreign/HeapMemorySegmentImpl.java b/src/java.base/share/classes/jdk/internal/foreign/HeapMemorySegmentImpl.java index e6b0ab7ab8de7..1791fda2d8711 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/HeapMemorySegmentImpl.java +++ b/src/java.base/share/classes/jdk/internal/foreign/HeapMemorySegmentImpl.java @@ -114,7 +114,7 @@ public static MemorySegment fromArray(byte[] arr) { Objects.requireNonNull(arr); long byteSize = (long)arr.length * Unsafe.ARRAY_BYTE_INDEX_SCALE; return new OfByte(Unsafe.ARRAY_BYTE_BASE_OFFSET, arr, byteSize, false, - MemorySessionImpl.heapSession(arr)); + MemorySessionImpl.createHeap(arr)); } @Override @@ -148,7 +148,7 @@ public static MemorySegment fromArray(char[] arr) { Objects.requireNonNull(arr); long byteSize = (long)arr.length * Unsafe.ARRAY_CHAR_INDEX_SCALE; return new OfChar(Unsafe.ARRAY_CHAR_BASE_OFFSET, arr, byteSize, false, - MemorySessionImpl.heapSession(arr)); + MemorySessionImpl.createHeap(arr)); } @Override @@ -182,7 +182,7 @@ public static MemorySegment fromArray(short[] arr) { Objects.requireNonNull(arr); long byteSize = (long)arr.length * Unsafe.ARRAY_SHORT_INDEX_SCALE; return new OfShort(Unsafe.ARRAY_SHORT_BASE_OFFSET, arr, byteSize, false, - MemorySessionImpl.heapSession(arr)); + MemorySessionImpl.createHeap(arr)); } @Override @@ -216,7 +216,7 @@ public static MemorySegment fromArray(int[] arr) { Objects.requireNonNull(arr); long byteSize = (long)arr.length * Unsafe.ARRAY_INT_INDEX_SCALE; return new OfInt(Unsafe.ARRAY_INT_BASE_OFFSET, arr, byteSize, false, - MemorySessionImpl.heapSession(arr)); + MemorySessionImpl.createHeap(arr)); } @Override @@ -250,7 +250,7 @@ public static MemorySegment fromArray(long[] arr) { Objects.requireNonNull(arr); long byteSize = (long)arr.length * Unsafe.ARRAY_LONG_INDEX_SCALE; return new OfLong(Unsafe.ARRAY_LONG_BASE_OFFSET, arr, byteSize, false, - MemorySessionImpl.heapSession(arr)); + MemorySessionImpl.createHeap(arr)); } @Override @@ -284,7 +284,7 @@ public static MemorySegment fromArray(float[] arr) { Objects.requireNonNull(arr); long byteSize = (long)arr.length * Unsafe.ARRAY_FLOAT_INDEX_SCALE; return new OfFloat(Unsafe.ARRAY_FLOAT_BASE_OFFSET, arr, byteSize, false, - MemorySessionImpl.heapSession(arr)); + MemorySessionImpl.createHeap(arr)); } @Override @@ -318,7 +318,7 @@ public static MemorySegment fromArray(double[] arr) { Objects.requireNonNull(arr); long byteSize = (long)arr.length * Unsafe.ARRAY_DOUBLE_INDEX_SCALE; return new OfDouble(Unsafe.ARRAY_DOUBLE_BASE_OFFSET, arr, byteSize, false, - MemorySessionImpl.heapSession(arr)); + MemorySessionImpl.createHeap(arr)); } @Override diff --git a/src/java.base/share/classes/jdk/internal/foreign/MappedMemorySegmentImpl.java b/src/java.base/share/classes/jdk/internal/foreign/MappedMemorySegmentImpl.java index 0c9482d8d3698..ead56fd1ea3d6 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/MappedMemorySegmentImpl.java +++ b/src/java.base/share/classes/jdk/internal/foreign/MappedMemorySegmentImpl.java @@ -48,8 +48,7 @@ public MappedMemorySegmentImpl(long min, UnmapperProxy unmapper, long length, bo @Override ByteBuffer makeByteBuffer() { - return NIO_ACCESS.newMappedByteBuffer(unmapper, min, (int)length, null, - scope == MemorySessionImpl.GLOBAL ? null : this); + return NIO_ACCESS.newMappedByteBuffer(unmapper, min, (int)length, null, this); } @Override diff --git a/src/java.base/share/classes/jdk/internal/foreign/MemorySessionImpl.java b/src/java.base/share/classes/jdk/internal/foreign/MemorySessionImpl.java index c8c7dd2e54364..33bac79999f28 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/MemorySessionImpl.java +++ b/src/java.base/share/classes/jdk/internal/foreign/MemorySessionImpl.java @@ -33,6 +33,8 @@ import java.lang.invoke.VarHandle; import java.lang.ref.Cleaner; import java.util.Objects; + +import jdk.internal.foreign.GlobalSession.HeapSession; import jdk.internal.misc.ScopedMemoryAccess; import jdk.internal.vm.annotation.ForceInline; @@ -59,10 +61,10 @@ public abstract sealed class MemorySessionImpl static final VarHandle STATE; static final int MAX_FORKS = Integer.MAX_VALUE; - public static final MemorySessionImpl GLOBAL = new GlobalSession(null); - static final ScopedMemoryAccess.ScopedAccessError ALREADY_CLOSED = new ScopedMemoryAccess.ScopedAccessError(MemorySessionImpl::alreadyClosed); static final ScopedMemoryAccess.ScopedAccessError WRONG_THREAD = new ScopedMemoryAccess.ScopedAccessError(MemorySessionImpl::wrongThread); + // This is the session of all zero-length memory segments + public static final MemorySessionImpl GLOBAL_SESSION = new GlobalSession(); final ResourceList resourceList; final Thread owner; @@ -143,6 +145,10 @@ public static MemorySessionImpl createImplicit(Cleaner cleaner) { return new ImplicitSession(cleaner); } + public static MemorySessionImpl createHeap(Object ref) { + return new HeapSession(ref); + } + public abstract void release0(); public abstract void acquire0(); @@ -230,10 +236,6 @@ public void close() { abstract void justClose(); - public static MemorySessionImpl heapSession(Object ref) { - return new GlobalSession(ref); - } - /** * A list of all cleanup actions associated with a memory session. Cleanup actions are modelled as instances * of the {@link ResourceCleanup} class, and, together, form a linked list. Depending on whether a session diff --git a/src/java.base/share/classes/jdk/internal/foreign/NativeMemorySegmentImpl.java b/src/java.base/share/classes/jdk/internal/foreign/NativeMemorySegmentImpl.java index 6575388b02597..bd29f0fd89060 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/NativeMemorySegmentImpl.java +++ b/src/java.base/share/classes/jdk/internal/foreign/NativeMemorySegmentImpl.java @@ -65,7 +65,7 @@ public sealed class NativeMemorySegmentImpl extends AbstractMemorySegmentImpl pe */ @ForceInline public NativeMemorySegmentImpl() { - super(0L, false, new GlobalSession(null)); + super(0L, false, MemorySessionImpl.GLOBAL_SESSION); this.min = 0L; } @@ -87,8 +87,7 @@ NativeMemorySegmentImpl dup(long offset, long size, boolean readOnly, MemorySess @Override ByteBuffer makeByteBuffer() { - return NIO_ACCESS.newDirectByteBuffer(min, (int) this.length, null, - scope == MemorySessionImpl.GLOBAL ? null : this); + return NIO_ACCESS.newDirectByteBuffer(min, (int) this.length, null, this); } @Override @@ -176,6 +175,6 @@ public static MemorySegment makeNativeSegmentUnchecked(long min, long byteSize, @ForceInline public static MemorySegment makeNativeSegmentUnchecked(long min, long byteSize) { - return new NativeMemorySegmentImpl(min, byteSize, false, new GlobalSession(null)); + return new NativeMemorySegmentImpl(min, byteSize, false, MemorySessionImpl.GLOBAL_SESSION); } }