From 0f3e2cc334e5926d53bbbce22e4a6bfeb2752140 Mon Sep 17 00:00:00 2001 From: Alan Bateman Date: Tue, 28 May 2024 15:05:54 +0000 Subject: [PATCH] 8331670: Deprecate the Memory-Access Methods in sun.misc.Unsafe for Removal Reviewed-by: mcimadamore, jpai, pminborg --- make/test/BuildMicrobenchmark.gmk | 2 +- src/hotspot/share/runtime/arguments.cpp | 9 + .../launcher/resources/launcher.properties | 6 +- src/java.base/share/man/java.1 | 24 + .../share/classes/sun/misc/Unsafe.java | 835 ++++++++++++++++-- test/jdk/sun/misc/TryUnsafeMemoryAccess.java | 136 +++ .../sun/misc/UnsafeMemoryAccessWarnings.java | 212 +++++ .../org/openjdk/bench/sun/misc/UnsafeOps.java | 89 ++ 8 files changed, 1229 insertions(+), 84 deletions(-) create mode 100644 test/jdk/sun/misc/TryUnsafeMemoryAccess.java create mode 100644 test/jdk/sun/misc/UnsafeMemoryAccessWarnings.java create mode 100644 test/micro/org/openjdk/bench/sun/misc/UnsafeOps.java diff --git a/make/test/BuildMicrobenchmark.gmk b/make/test/BuildMicrobenchmark.gmk index 7b65e89610e..32d26be2270 100644 --- a/make/test/BuildMicrobenchmark.gmk +++ b/make/test/BuildMicrobenchmark.gmk @@ -94,7 +94,7 @@ $(eval $(call SetupJavaCompilation, BUILD_JDK_MICROBENCHMARK, \ TARGET_RELEASE := $(TARGET_RELEASE_NEWJDK_UPGRADED), \ SMALL_JAVA := false, \ CLASSPATH := $(JMH_COMPILE_JARS), \ - DISABLED_WARNINGS := restricted this-escape processing rawtypes cast \ + DISABLED_WARNINGS := restricted this-escape processing rawtypes removal cast \ serial preview dangling-doc-comments, \ SRC := $(MICROBENCHMARK_SRC), \ BIN := $(MICROBENCHMARK_CLASSES), \ diff --git a/src/hotspot/share/runtime/arguments.cpp b/src/hotspot/share/runtime/arguments.cpp index 7a74ef6e248..7befe9cd734 100644 --- a/src/hotspot/share/runtime/arguments.cpp +++ b/src/hotspot/share/runtime/arguments.cpp @@ -2282,6 +2282,15 @@ jint Arguments::parse_each_vm_init_arg(const JavaVMInitArgs* args, bool* patch_m if (res != JNI_OK) { return res; } + } else if (match_option(option, "--sun-misc-unsafe-memory-access=", &tail)) { + if (strcmp(tail, "allow") == 0 || strcmp(tail, "warn") == 0 || strcmp(tail, "debug") == 0 || strcmp(tail, "deny") == 0) { + PropertyList_unique_add(&_system_properties, "sun.misc.unsafe.memory.access", tail, + AddProperty, WriteableProperty, InternalProperty); + } else { + jio_fprintf(defaultStream::error_stream(), + "Value specified to --sun-misc-unsafe-memory-access not recognized: '%s'\n", tail); + return JNI_ERR; + } } else if (match_option(option, "--illegal-access=", &tail)) { char version[256]; JDK_Version::jdk(17).to_string(version, sizeof(version)); diff --git a/src/java.base/share/classes/sun/launcher/resources/launcher.properties b/src/java.base/share/classes/sun/launcher/resources/launcher.properties index 489b1395a22..cd524955419 100644 --- a/src/java.base/share/classes/sun/launcher/resources/launcher.properties +++ b/src/java.base/share/classes/sun/launcher/resources/launcher.properties @@ -212,7 +212,11 @@ java.launcher.X.usage=\n\ \ --finalization=\n\ \ controls whether the JVM performs finalization of objects,\n\ \ where is one of "enabled" or "disabled".\n\ -\ Finalization is enabled by default.\n\n\ +\ Finalization is enabled by default.\n\ +\ --sun-misc-unsafe-memory-access=\n\ +\ allow or deny usage of unsupported API sun.misc.Unsafe\n\ +\ is one of "allow", "warn", "debug", or "deny".\n\ +\ The default value is "allow".\n\n\ These extra options are subject to change without notice.\n # Translators please note do not translate the options themselves diff --git a/src/java.base/share/man/java.1 b/src/java.base/share/man/java.1 index 990fdccf8d5..6cef707d19a 100644 --- a/src/java.base/share/man/java.1 +++ b/src/java.base/share/man/java.1 @@ -1194,6 +1194,30 @@ or directories. .TP \f[V]--source\f[R] \f[I]version\f[R] Sets the version of the source in source-file mode. +.TP +\f[V]--sun-misc-unsafe-memory-acces=\f[R] \f[I]value\f[R] +Allow or deny usage of unsupported API \f[V]sun.misc.Unsafe\f[R]. +\f[I]value\f[R] is one of: +.RS +.TP +\f[V]allow\f[R] +Allow use of the memory-access methods with no warnings at run time. +.TP +\f[V]warn\f[R] +Allow use of the memory-access methods, but issues a warning on the +first occasion that any memory-access method is used. +At most one warning is issued. +.TP +\f[V]debug\f[R] +Allow use of the memory-access methods, but issue a one-line warning and +a stack trace when any memory-access method is used. +.TP +\f[V]deny\f[R] +Disallow use of the memory-access methods by throwing an +\f[V]UnsupportedOperationException\f[R] on every usage. +.PP +The default value when the option is not specified is \f[V]allow\f[R]. +.RE .SH EXTRA OPTIONS FOR MACOS .PP The following extra options are macOS specific. diff --git a/src/jdk.unsupported/share/classes/sun/misc/Unsafe.java b/src/jdk.unsupported/share/classes/sun/misc/Unsafe.java index 3f91fd70db4..e1a936117d4 100644 --- a/src/jdk.unsupported/share/classes/sun/misc/Unsafe.java +++ b/src/jdk.unsupported/share/classes/sun/misc/Unsafe.java @@ -25,16 +25,26 @@ package sun.misc; +import java.lang.foreign.Arena; +import java.lang.foreign.MemoryLayout; +import java.lang.foreign.MemorySegment; +import java.lang.foreign.ValueLayout; +import java.lang.invoke.VarHandle; +import java.lang.reflect.Field; +import java.net.URL; +import java.security.AccessController; +import java.security.CodeSource; +import java.security.ProtectionDomain; +import java.security.PrivilegedAction; +import java.util.List; +import java.util.Set; + import jdk.internal.vm.annotation.ForceInline; +import jdk.internal.vm.annotation.Stable; import jdk.internal.misc.VM; import jdk.internal.reflect.CallerSensitive; import jdk.internal.reflect.Reflection; -import java.lang.invoke.MethodHandles; -import java.lang.reflect.Field; -import java.util.Set; - - /** * A collection of methods for performing low-level, unsafe operations. * Although the class and all methods are public, use of this class is @@ -49,6 +59,11 @@ import java.util.Set; * the caller must not rely on the checks and corresponding * exceptions! * + * @apiNote + * This class pre-dates the introduction of {@link VarHandle}, low-level access to + * memory with {@linkplain java.lang.foreign}, and other standard APIs. New code + * should not use this API. + * * @author John R. Rose * @see #getUnsafe */ @@ -150,6 +165,9 @@ public final class Unsafe { * be portably confused with longs used in the single-register addressing * mode. * + * @deprecated Use {@link VarHandle#get(Object...)} or + * {@link MemorySegment#get(ValueLayout.OfInt, long)} instead. + * * @param o Java heap object in which the variable resides, if any, else * null * @param offset indication of where the variable resides in a Java heap @@ -159,8 +177,10 @@ public final class Unsafe { * @throws RuntimeException No defined exceptions are thrown, not even * {@link NullPointerException} */ + @Deprecated(since="23", forRemoval=true) @ForceInline public int getInt(Object o, long offset) { + beforeMemoryAccess(); return theInternalUnsafe.getInt(o, offset); } @@ -175,6 +195,9 @@ public final class Unsafe { * The variable must be of the same type as the method * parameter {@code x}. * + * @deprecated Use {@link VarHandle#set(Object...)} or + * {@link MemorySegment#set(ValueLayout.OfInt, long, int)} instead. + * * @param o Java heap object in which the variable resides, if any, else * null * @param offset indication of where the variable resides in a Java heap @@ -184,17 +207,22 @@ public final class Unsafe { * @throws RuntimeException No defined exceptions are thrown, not even * {@link NullPointerException} */ + @Deprecated(since="23", forRemoval=true) @ForceInline public void putInt(Object o, long offset, int x) { + beforeMemoryAccess(); theInternalUnsafe.putInt(o, offset, x); } /** * Fetches a reference value from a given Java variable. - * @see #getInt(Object, long) + * + * @deprecated Use {@link VarHandle#get(Object...)} instead. */ + @Deprecated(since="23", forRemoval=true) @ForceInline public Object getObject(Object o, long offset) { + beforeMemoryAccess(); return theInternalUnsafe.getReference(o, offset); } @@ -206,94 +234,195 @@ public final class Unsafe { * If the reference {@code o} is non-null, card marks or * other store barriers for that object (if the VM requires them) * are updated. - * @see #putInt(Object, long, int) + * + * @deprecated Use {@link VarHandle#set(Object...)} instead. */ + @Deprecated(since="23", forRemoval=true) @ForceInline public void putObject(Object o, long offset, Object x) { + beforeMemoryAccess(); theInternalUnsafe.putReference(o, offset, x); } - /** @see #getInt(Object, long) */ + /** + * @deprecated Use {@link VarHandle#get(Object...)} or + * {@link MemorySegment#get(ValueLayout.OfBoolean, long)} instead. + * + * @see #getInt(Object, long) + */ + @Deprecated(since="23", forRemoval=true) @ForceInline public boolean getBoolean(Object o, long offset) { + beforeMemoryAccess(); return theInternalUnsafe.getBoolean(o, offset); } - /** @see #putInt(Object, long, int) */ + /** + * @deprecated Use {@link VarHandle#set(Object...)} or + * {@link MemorySegment#set(ValueLayout.OfBoolean, long, boolean)} instead. + * + * @see #putInt(Object, long, int) + */ + @Deprecated(since="23", forRemoval=true) @ForceInline public void putBoolean(Object o, long offset, boolean x) { + beforeMemoryAccess(); theInternalUnsafe.putBoolean(o, offset, x); } - /** @see #getInt(Object, long) */ + /** + * @deprecated Use {@link VarHandle#get(Object...)} or + * {@link MemorySegment#get(ValueLayout.OfByte, long)} instead. + * + * @see #getInt(Object, long) + */ + @Deprecated(since="23", forRemoval=true) @ForceInline public byte getByte(Object o, long offset) { + beforeMemoryAccess(); return theInternalUnsafe.getByte(o, offset); } - /** @see #putInt(Object, long, int) */ + /** + * @deprecated Use {@link VarHandle#set(Object...)} or + * {@link MemorySegment#set(ValueLayout.OfByte, long, byte)} instead. + * + * @see #putInt(Object, long, int) + */ + @Deprecated(since="23", forRemoval=true) @ForceInline public void putByte(Object o, long offset, byte x) { + beforeMemoryAccess(); theInternalUnsafe.putByte(o, offset, x); } - /** @see #getInt(Object, long) */ + /** + * @deprecated Use {@link VarHandle#get(Object...)} or + * {@link MemorySegment#get(ValueLayout.OfShort, long)} instead. + * + * @see #getInt(Object, long) + */ + @Deprecated(since="23", forRemoval=true) @ForceInline public short getShort(Object o, long offset) { + beforeMemoryAccess(); return theInternalUnsafe.getShort(o, offset); } - /** @see #putInt(Object, long, int) */ + /** + * @deprecated Use {@link VarHandle#set(Object...)} or + * {@link MemorySegment#set(ValueLayout.OfShort, long, short)} instead. + * + * @see #putInt(Object, long, int) + */ + @Deprecated(since="23", forRemoval=true) @ForceInline public void putShort(Object o, long offset, short x) { + beforeMemoryAccess(); theInternalUnsafe.putShort(o, offset, x); } - /** @see #getInt(Object, long) */ + /** + * @deprecated Use {@link VarHandle#get(Object...)} or + * {@link MemorySegment#get(ValueLayout.OfChar, long)} instead. + * + * @see #getInt(Object, long) + */ + @Deprecated(since="23", forRemoval=true) @ForceInline public char getChar(Object o, long offset) { + beforeMemoryAccess(); return theInternalUnsafe.getChar(o, offset); } - /** @see #putInt(Object, long, int) */ + /** + * @deprecated Use {@link VarHandle#set(Object...)} or + * {@link MemorySegment#set(ValueLayout.OfChar, long, char)} instead. + * + * @see #putInt(Object, long, int) + */ + @Deprecated(since="23", forRemoval=true) @ForceInline public void putChar(Object o, long offset, char x) { + beforeMemoryAccess(); theInternalUnsafe.putChar(o, offset, x); } - /** @see #getInt(Object, long) */ + /** + * @deprecated Use {@link VarHandle#get(Object...)} or + * {@link MemorySegment#get(ValueLayout.OfLong, long)} instead. + * + * @see #getInt(Object, long) + */ + @Deprecated(since="23", forRemoval=true) @ForceInline public long getLong(Object o, long offset) { + beforeMemoryAccess(); return theInternalUnsafe.getLong(o, offset); } - /** @see #putInt(Object, long, int) */ + /** + * @deprecated Use {@link VarHandle#set(Object...)} or + * {@link MemorySegment#set(ValueLayout.OfLong, long, long)} instead. + * + * @see #putInt(Object, long, int) + */ + @Deprecated(since="23", forRemoval=true) @ForceInline public void putLong(Object o, long offset, long x) { + beforeMemoryAccess(); theInternalUnsafe.putLong(o, offset, x); } - /** @see #getInt(Object, long) */ + /** + * @deprecated Use {@link VarHandle#get(Object...)} or + * {@link MemorySegment#get(ValueLayout.OfFloat, long)} instead. + * + * @see #getInt(Object, long) + */ + @Deprecated(since="23", forRemoval=true) @ForceInline public float getFloat(Object o, long offset) { + beforeMemoryAccess(); return theInternalUnsafe.getFloat(o, offset); } - /** @see #putInt(Object, long, int) */ + /** + * @deprecated Use {@link VarHandle#set(Object...)} or + * {@link MemorySegment#set(ValueLayout.OfFloat, long, float)} instead. + * + * @see #putInt(Object, long, int) + */ + @Deprecated(since="23", forRemoval=true) @ForceInline public void putFloat(Object o, long offset, float x) { + beforeMemoryAccess(); theInternalUnsafe.putFloat(o, offset, x); } - /** @see #getInt(Object, long) */ + /** + * @deprecated Use {@link VarHandle#get(Object...)} or + * {@link MemorySegment#get(ValueLayout.OfDouble, long)} instead. + * + * @see #getInt(Object, long) + */ + @Deprecated(since="23", forRemoval=true) @ForceInline public double getDouble(Object o, long offset) { + beforeMemoryAccess(); return theInternalUnsafe.getDouble(o, offset); } - /** @see #putInt(Object, long, int) */ + /** + * @deprecated Use {@link VarHandle#set(Object...)} or + * {@link MemorySegment#set(ValueLayout.OfDouble, long, double)} instead. + * + * @see #putInt(Object, long, int) + */ + @Deprecated(since="23", forRemoval=true) @ForceInline public void putDouble(Object o, long offset, double x) { + beforeMemoryAccess(); theInternalUnsafe.putDouble(o, offset, x); } @@ -304,10 +433,14 @@ public final class Unsafe { * does not point into a block obtained from {@link #allocateMemory}, the * results are undefined. * + * @deprecated Use {@link java.lang.foreign} to access off-heap memory. + * * @see #allocateMemory */ + @Deprecated(since="23", forRemoval=true) @ForceInline public byte getByte(long address) { + beforeMemoryAccess(); return theInternalUnsafe.getByte(address); } @@ -316,82 +449,158 @@ public final class Unsafe { * does not point into a block obtained from {@link #allocateMemory}, the * results are undefined. * + * @deprecated Use {@link java.lang.foreign} to access off-heap memory. + * * @see #getByte(long) */ + @Deprecated(since="23", forRemoval=true) @ForceInline public void putByte(long address, byte x) { + beforeMemoryAccess(); theInternalUnsafe.putByte(address, x); } - /** @see #getByte(long) */ + /** + * @deprecated Use {@link java.lang.foreign} to access off-heap memory. + * + * @see #getByte(long) + */ + @Deprecated(since="23", forRemoval=true) @ForceInline public short getShort(long address) { + beforeMemoryAccess(); return theInternalUnsafe.getShort(address); } - /** @see #putByte(long, byte) */ + /** + * @deprecated Use {@link java.lang.foreign} to access off-heap memory. + * + * @see #putByte(long, byte) + */ + @Deprecated(since="23", forRemoval=true) @ForceInline public void putShort(long address, short x) { + beforeMemoryAccess(); theInternalUnsafe.putShort(address, x); } - /** @see #getByte(long) */ + /** + * @deprecated Use {@link java.lang.foreign} to access off-heap memory. + * + * @see #getByte(long) + */ + @Deprecated(since="23", forRemoval=true) @ForceInline public char getChar(long address) { + beforeMemoryAccess(); return theInternalUnsafe.getChar(address); } - /** @see #putByte(long, byte) */ + /** + * @deprecated Use {@link java.lang.foreign} to access off-heap memory. + * + * @see #putByte(long, byte) + */ + @Deprecated(since="23", forRemoval=true) @ForceInline public void putChar(long address, char x) { + beforeMemoryAccess(); theInternalUnsafe.putChar(address, x); } - /** @see #getByte(long) */ + /** + * @deprecated Use {@link java.lang.foreign} to access off-heap memory. + * + * @see #getByte(long) + */ + @Deprecated(since="23", forRemoval=true) @ForceInline public int getInt(long address) { + beforeMemoryAccess(); return theInternalUnsafe.getInt(address); } - /** @see #putByte(long, byte) */ + /** + * @deprecated Use {@link java.lang.foreign} to access off-heap memory. + * + * @see #putByte(long, byte) + */ + @Deprecated(since="23", forRemoval=true) @ForceInline public void putInt(long address, int x) { + beforeMemoryAccess(); theInternalUnsafe.putInt(address, x); } - /** @see #getByte(long) */ + /** + * @deprecated Use {@link java.lang.foreign} to access off-heap memory. + * + * @see #getByte(long) + */ + @Deprecated(since="23", forRemoval=true) @ForceInline public long getLong(long address) { + beforeMemoryAccess(); return theInternalUnsafe.getLong(address); } - /** @see #putByte(long, byte) */ + /** + * @deprecated Use {@link java.lang.foreign} to access off-heap memory. + * + * @see #putByte(long, byte) + */ + @Deprecated(since="23", forRemoval=true) @ForceInline public void putLong(long address, long x) { + beforeMemoryAccess(); theInternalUnsafe.putLong(address, x); } - /** @see #getByte(long) */ + /** + * @deprecated Use {@link java.lang.foreign} to access off-heap memory. + * + * @see #getByte(long) + */ + @Deprecated(since="23", forRemoval=true) @ForceInline public float getFloat(long address) { + beforeMemoryAccess(); return theInternalUnsafe.getFloat(address); } - /** @see #putByte(long, byte) */ + /** + * @deprecated Use {@link java.lang.foreign} to access off-heap memory. + * + * @see #putByte(long, byte) + */ + @Deprecated(since="23", forRemoval=true) @ForceInline public void putFloat(long address, float x) { + beforeMemoryAccess(); theInternalUnsafe.putFloat(address, x); } - /** @see #getByte(long) */ + /** + * @deprecated Use {@link java.lang.foreign} to access off-heap memory. + * + * @see #getByte(long) + */ + @Deprecated(since="23", forRemoval=true) @ForceInline public double getDouble(long address) { + beforeMemoryAccess(); return theInternalUnsafe.getDouble(address); } - /** @see #putByte(long, byte) */ + /** + * @deprecated Use {@link java.lang.foreign} to access off-heap memory. + * + * @see #putByte(long, byte) + */ + @Deprecated(since="23", forRemoval=true) @ForceInline public void putDouble(long address, double x) { + beforeMemoryAccess(); theInternalUnsafe.putDouble(address, x); } @@ -408,10 +617,14 @@ public final class Unsafe { * from the target address may be determined by consulting {@link * #addressSize}. * + * @deprecated Use {@link java.lang.foreign} to access off-heap memory. + * * @see #allocateMemory */ + @Deprecated(since="23", forRemoval=true) @ForceInline public long getAddress(long address) { + beforeMemoryAccess(); return theInternalUnsafe.getAddress(address); } @@ -423,10 +636,14 @@ public final class Unsafe { *

The number of bytes actually written at the target address may be * determined by consulting {@link #addressSize}. * + * @deprecated Use {@link java.lang.foreign} to access off-heap memory. + * * @see #getAddress(long) */ + @Deprecated(since="23", forRemoval=true) @ForceInline public void putAddress(long address, long x) { + beforeMemoryAccess(); theInternalUnsafe.putAddress(address, x); } @@ -450,6 +667,8 @@ public final class Unsafe { * caller must not rely on the checks and corresponding * exceptions! * + * @deprecated Use {@link java.lang.foreign} to allocate off-heap memory. + * * @throws RuntimeException if the size is negative or too large * for the native size_t type * @@ -458,8 +677,10 @@ public final class Unsafe { * @see #getByte(long) * @see #putByte(long, byte) */ + @Deprecated(since="23", forRemoval=true) @ForceInline public long allocateMemory(long bytes) { + beforeMemoryAccess(); return theInternalUnsafe.allocateMemory(bytes); } @@ -482,6 +703,8 @@ public final class Unsafe { * caller must not rely on the checks and corresponding * exceptions! * + * @deprecated Use {@link java.lang.foreign} to allocate off-heap memory. + * * @throws RuntimeException if the size is negative or too large * for the native size_t type * @@ -489,8 +712,10 @@ public final class Unsafe { * * @see #allocateMemory */ + @Deprecated(since="23", forRemoval=true) @ForceInline public long reallocateMemory(long address, long bytes) { + beforeMemoryAccess(); return theInternalUnsafe.reallocateMemory(address, bytes); } @@ -518,12 +743,17 @@ public final class Unsafe { * caller must not rely on the checks and corresponding * exceptions! * + * @deprecated {@link MemorySegment#fill(byte)} fills the contents of a memory + * segment with a given value. + * * @throws RuntimeException if any of the arguments is invalid * * @since 1.7 */ + @Deprecated(since="23", forRemoval=true) @ForceInline public void setMemory(Object o, long offset, long bytes, byte value) { + beforeMemoryAccess(); theInternalUnsafe.setMemory(o, offset, bytes, value); } @@ -533,9 +763,16 @@ public final class Unsafe { * as discussed in {@link #getInt(Object,long)}. * *

Equivalent to {@code setMemory(null, address, bytes, value)}. + * + * @deprecated {@link MemorySegment#fill(byte)} fills the contents of a memory + * segment with a given value. + * + * Use {@link MemorySegment} and its bulk copy methods instead. */ + @Deprecated(since="23", forRemoval=true) @ForceInline public void setMemory(long address, long bytes, byte value) { + beforeMemoryAccess(); theInternalUnsafe.setMemory(address, bytes, value); } @@ -563,14 +800,18 @@ public final class Unsafe { * caller must not rely on the checks and corresponding * exceptions! * + * @deprecated Use {@link MemorySegment} and its bulk copy methods instead. + * * @throws RuntimeException if any of the arguments is invalid * * @since 1.7 */ + @Deprecated(since="23", forRemoval=true) @ForceInline public void copyMemory(Object srcBase, long srcOffset, Object destBase, long destOffset, long bytes) { + beforeMemoryAccess(); theInternalUnsafe.copyMemory(srcBase, srcOffset, destBase, destOffset, bytes); } @@ -580,9 +821,13 @@ public final class Unsafe { * as discussed in {@link #getInt(Object,long)}. * * Equivalent to {@code copyMemory(null, srcAddress, null, destAddress, bytes)}. + * + * @deprecated Use {@link MemorySegment} and its bulk copy methods instead. */ + @Deprecated(since="23", forRemoval=true) @ForceInline public void copyMemory(long srcAddress, long destAddress, long bytes) { + beforeMemoryAccess(); theInternalUnsafe.copyMemory(srcAddress, destAddress, bytes); } @@ -600,12 +845,16 @@ public final class Unsafe { * caller must not rely on the checks and corresponding * exceptions! * + * @deprecated Use {@link java.lang.foreign} to allocate and free off-heap memory. + * * @throws RuntimeException if any of the arguments is invalid * * @see #allocateMemory */ + @Deprecated(since="23", forRemoval=true) @ForceInline public void freeMemory(long address) { + beforeMemoryAccess(); theInternalUnsafe.freeMemory(address); } @@ -615,7 +864,9 @@ public final class Unsafe { * This constant differs from all results that will ever be returned from * {@link #staticFieldOffset}, {@link #objectFieldOffset}, * or {@link #arrayBaseOffset}. + * @deprecated Not needed when using {@link VarHandle} or {@link java.lang.foreign}. */ + @Deprecated(since="23", forRemoval=true) public static final int INVALID_FIELD_OFFSET = jdk.internal.misc.Unsafe.INVALID_FIELD_OFFSET; /** @@ -638,11 +889,11 @@ public final class Unsafe { * @deprecated The guarantee that a field will always have the same offset * and base may not be true in a future release. The ability to provide an * offset and object reference to a heap memory accessor will be removed - * in a future release. Use {@link java.lang.invoke.VarHandle} instead. + * in a future release. Use {@link VarHandle} instead. * * @see #getInt(Object, long) */ - @Deprecated(since="18") + @Deprecated(since="18", forRemoval=true) @ForceInline public long objectFieldOffset(Field f) { if (f == null) { @@ -655,6 +906,7 @@ public final class Unsafe { if (declaringClass.isRecord()) { throw new UnsupportedOperationException("can't get field offset on a record class: " + f); } + beforeMemoryAccess(); return theInternalUnsafe.objectFieldOffset(f); } @@ -677,11 +929,11 @@ public final class Unsafe { * @deprecated The guarantee that a field will always have the same offset * and base may not be true in a future release. The ability to provide an * offset and object reference to a heap memory accessor will be removed - * in a future release. Use {@link java.lang.invoke.VarHandle} instead. + * in a future release. Use {@link VarHandle} instead. * * @see #getInt(Object, long) */ - @Deprecated(since="18") + @Deprecated(since="18", forRemoval=true) @ForceInline public long staticFieldOffset(Field f) { if (f == null) { @@ -694,6 +946,7 @@ public final class Unsafe { if (declaringClass.isRecord()) { throw new UnsupportedOperationException("can't get field offset on a record class: " + f); } + beforeMemoryAccess(); return theInternalUnsafe.staticFieldOffset(f); } @@ -710,9 +963,9 @@ public final class Unsafe { * @deprecated The guarantee that a field will always have the same offset * and base may not be true in a future release. The ability to provide an * offset and object reference to a heap memory accessor will be removed - * in a future release. Use {@link java.lang.invoke.VarHandle} instead. + * in a future release. Use {@link VarHandle} instead. */ - @Deprecated(since="18") + @Deprecated(since="18", forRemoval=true) @ForceInline public Object staticFieldBase(Field f) { if (f == null) { @@ -725,6 +978,7 @@ public final class Unsafe { if (declaringClass.isRecord()) { throw new UnsupportedOperationException("can't get base address on a record class: " + f); } + beforeMemoryAccess(); return theInternalUnsafe.staticFieldBase(f); } @@ -735,39 +989,79 @@ public final class Unsafe { * base offset, to form new offsets to access elements of arrays of the * given class. * + * @deprecated Not needed when using {@link VarHandle} or {@link java.lang.foreign}. + * * @see #getInt(Object, long) * @see #putInt(Object, long, int) */ + @Deprecated(since="23", forRemoval=true) @ForceInline public int arrayBaseOffset(Class arrayClass) { + beforeMemoryAccess(); return theInternalUnsafe.arrayBaseOffset(arrayClass); } - /** The value of {@code arrayBaseOffset(boolean[].class)} */ + /** The value of {@code arrayBaseOffset(boolean[].class)}. + * + * @deprecated Not needed when using {@link VarHandle} or {@link java.lang.foreign}. + */ + @Deprecated(since="23", forRemoval=true) public static final int ARRAY_BOOLEAN_BASE_OFFSET = jdk.internal.misc.Unsafe.ARRAY_BOOLEAN_BASE_OFFSET; - /** The value of {@code arrayBaseOffset(byte[].class)} */ + /** The value of {@code arrayBaseOffset(byte[].class)}. + * + * @deprecated Not needed when using {@link VarHandle} or {@link java.lang.foreign}. + */ + @Deprecated(since="23", forRemoval=true) public static final int ARRAY_BYTE_BASE_OFFSET = jdk.internal.misc.Unsafe.ARRAY_BYTE_BASE_OFFSET; - /** The value of {@code arrayBaseOffset(short[].class)} */ + /** The value of {@code arrayBaseOffset(short[].class)}. + * + * @deprecated Not needed when using {@link VarHandle} or {@link java.lang.foreign}. + */ + @Deprecated(since="23", forRemoval=true) public static final int ARRAY_SHORT_BASE_OFFSET = jdk.internal.misc.Unsafe.ARRAY_SHORT_BASE_OFFSET; - /** The value of {@code arrayBaseOffset(char[].class)} */ + /** The value of {@code arrayBaseOffset(char[].class)}. + * + * @deprecated Not needed when using {@link VarHandle} or {@link java.lang.foreign}. + */ + @Deprecated(since="23", forRemoval=true) public static final int ARRAY_CHAR_BASE_OFFSET = jdk.internal.misc.Unsafe.ARRAY_CHAR_BASE_OFFSET; - /** The value of {@code arrayBaseOffset(int[].class)} */ + /** The value of {@code arrayBaseOffset(int[].class)}. + * + * @deprecated Not needed when using {@link VarHandle} or {@link java.lang.foreign}. + */ + @Deprecated(since="23", forRemoval=true) public static final int ARRAY_INT_BASE_OFFSET = jdk.internal.misc.Unsafe.ARRAY_INT_BASE_OFFSET; - /** The value of {@code arrayBaseOffset(long[].class)} */ + /** The value of {@code arrayBaseOffset(long[].class)}. + * + * @deprecated Not needed when using {@link VarHandle} or {@link java.lang.foreign}. + */ + @Deprecated(since="23", forRemoval=true) public static final int ARRAY_LONG_BASE_OFFSET = jdk.internal.misc.Unsafe.ARRAY_LONG_BASE_OFFSET; - /** The value of {@code arrayBaseOffset(float[].class)} */ + /** The value of {@code arrayBaseOffset(float[].class)}. + * + * @deprecated Not needed when using {@link VarHandle} or {@link java.lang.foreign}. + */ + @Deprecated(since="23", forRemoval=true) public static final int ARRAY_FLOAT_BASE_OFFSET = jdk.internal.misc.Unsafe.ARRAY_FLOAT_BASE_OFFSET; - /** The value of {@code arrayBaseOffset(double[].class)} */ + /** The value of {@code arrayBaseOffset(double[].class)}. + * + * @deprecated Not needed when using {@link VarHandle} or {@link java.lang.foreign}. + */ + @Deprecated(since="23", forRemoval=true) public static final int ARRAY_DOUBLE_BASE_OFFSET = jdk.internal.misc.Unsafe.ARRAY_DOUBLE_BASE_OFFSET; - /** The value of {@code arrayBaseOffset(Object[].class)} */ + /** The value of {@code arrayBaseOffset(Object[].class)}. + * + * @deprecated Not needed when using {@link VarHandle} or {@link java.lang.foreign}. + */ + @Deprecated(since="23", forRemoval=true) public static final int ARRAY_OBJECT_BASE_OFFSET = jdk.internal.misc.Unsafe.ARRAY_OBJECT_BASE_OFFSET; /** @@ -777,40 +1071,79 @@ public final class Unsafe { * #getByte(Object, long)}, so the scale factor for such classes is reported * as zero. * + * @deprecated Not needed when using {@link VarHandle} or {@link java.lang.foreign}. + * * @see #arrayBaseOffset * @see #getInt(Object, long) * @see #putInt(Object, long, int) */ + @Deprecated(since="23", forRemoval=true) @ForceInline public int arrayIndexScale(Class arrayClass) { return theInternalUnsafe.arrayIndexScale(arrayClass); } - /** The value of {@code arrayIndexScale(boolean[].class)} */ + /** The value of {@code arrayIndexScale(boolean[].class)}. + * + * @deprecated Not needed when using {@link VarHandle} or {@link java.lang.foreign}. + */ + @Deprecated(since="23", forRemoval=true) public static final int ARRAY_BOOLEAN_INDEX_SCALE = jdk.internal.misc.Unsafe.ARRAY_BOOLEAN_INDEX_SCALE; - /** The value of {@code arrayIndexScale(byte[].class)} */ + /** The value of {@code arrayIndexScale(byte[].class)}. + * + * @deprecated Not needed when using {@link VarHandle} or {@link java.lang.foreign}. + */ + @Deprecated(since="23", forRemoval=true) public static final int ARRAY_BYTE_INDEX_SCALE = jdk.internal.misc.Unsafe.ARRAY_BYTE_INDEX_SCALE; - /** The value of {@code arrayIndexScale(short[].class)} */ + /** The value of {@code arrayIndexScale(short[].class)}. + * + * @deprecated Not needed when using {@link VarHandle} or {@link java.lang.foreign}. + */ + @Deprecated(since="23", forRemoval=true) public static final int ARRAY_SHORT_INDEX_SCALE = jdk.internal.misc.Unsafe.ARRAY_SHORT_INDEX_SCALE; - /** The value of {@code arrayIndexScale(char[].class)} */ + /** The value of {@code arrayIndexScale(char[].class)}. + * + * @deprecated Not needed when using {@link VarHandle} or {@link java.lang.foreign}. + */ + @Deprecated(since="23", forRemoval=true) public static final int ARRAY_CHAR_INDEX_SCALE = jdk.internal.misc.Unsafe.ARRAY_CHAR_INDEX_SCALE; - /** The value of {@code arrayIndexScale(int[].class)} */ + /** The value of {@code arrayIndexScale(int[].class)}. + * + * @deprecated Not needed when using {@link VarHandle} or {@link java.lang.foreign}. + */ + @Deprecated(since="23", forRemoval=true) public static final int ARRAY_INT_INDEX_SCALE = jdk.internal.misc.Unsafe.ARRAY_INT_INDEX_SCALE; - /** The value of {@code arrayIndexScale(long[].class)} */ + /** The value of {@code arrayIndexScale(long[].class)}. + * + * @deprecated Not needed when using {@link VarHandle} or {@link java.lang.foreign}. + */ + @Deprecated(since="23", forRemoval=true) public static final int ARRAY_LONG_INDEX_SCALE = jdk.internal.misc.Unsafe.ARRAY_LONG_INDEX_SCALE; - /** The value of {@code arrayIndexScale(float[].class)} */ + /** The value of {@code arrayIndexScale(float[].class)}. + * + * @deprecated Not needed when using {@link VarHandle} or {@link java.lang.foreign}. + */ + @Deprecated(since="23", forRemoval=true) public static final int ARRAY_FLOAT_INDEX_SCALE = jdk.internal.misc.Unsafe.ARRAY_FLOAT_INDEX_SCALE; - /** The value of {@code arrayIndexScale(double[].class)} */ + /** The value of {@code arrayIndexScale(double[].class)}. + * + * @deprecated Not needed when using {@link VarHandle} or {@link java.lang.foreign}. + */ + @Deprecated(since="23", forRemoval=true) public static final int ARRAY_DOUBLE_INDEX_SCALE = jdk.internal.misc.Unsafe.ARRAY_DOUBLE_INDEX_SCALE; - /** The value of {@code arrayIndexScale(Object[].class)} */ + /** The value of {@code arrayIndexScale(Object[].class)}. + * + * @deprecated Not needed when using {@link VarHandle} or {@link java.lang.foreign}. + */ + @Deprecated(since="23", forRemoval=true) public static final int ARRAY_OBJECT_INDEX_SCALE = jdk.internal.misc.Unsafe.ARRAY_OBJECT_INDEX_SCALE; /** @@ -818,13 +1151,20 @@ public final class Unsafe { * #putAddress}. This value will be either 4 or 8. Note that the sizes of * other primitive types (as stored in native memory blocks) is determined * fully by their information content. + * + * @deprecated Use {@link ValueLayout#ADDRESS}.{@link MemoryLayout#byteSize()} instead. */ + @Deprecated(since="23", forRemoval=true) @ForceInline public int addressSize() { return theInternalUnsafe.addressSize(); } - /** The value of {@code addressSize()} */ + /** The value of {@code addressSize()}. + * + * @deprecated Use {@link ValueLayout#ADDRESS}.{@link MemoryLayout#byteSize()} instead. + */ + @Deprecated(since="23", forRemoval=true) public static final int ADDRESS_SIZE = theInternalUnsafe.addressSize(); /** @@ -863,11 +1203,15 @@ public final class Unsafe { * and write. Corresponds to C11 atomic_compare_exchange_strong. * * @return {@code true} if successful + * + * @deprecated Use {@link VarHandle#compareAndExchange(Object...)} instead. */ + @Deprecated(since="23", forRemoval=true) @ForceInline public final boolean compareAndSwapObject(Object o, long offset, Object expected, Object x) { + beforeMemoryAccess(); return theInternalUnsafe.compareAndSetReference(o, offset, expected, x); } @@ -879,11 +1223,15 @@ public final class Unsafe { * and write. Corresponds to C11 atomic_compare_exchange_strong. * * @return {@code true} if successful + * + * @deprecated Use {@link VarHandle#compareAndExchange(Object...)} instead. */ + @Deprecated(since="23", forRemoval=true) @ForceInline public final boolean compareAndSwapInt(Object o, long offset, int expected, int x) { + beforeMemoryAccess(); return theInternalUnsafe.compareAndSetInt(o, offset, expected, x); } @@ -895,125 +1243,218 @@ public final class Unsafe { * and write. Corresponds to C11 atomic_compare_exchange_strong. * * @return {@code true} if successful + * + * @deprecated Use {@link VarHandle#compareAndExchange(Object...)} instead. */ + @Deprecated(since="23", forRemoval=true) @ForceInline public final boolean compareAndSwapLong(Object o, long offset, long expected, long x) { + beforeMemoryAccess(); return theInternalUnsafe.compareAndSetLong(o, offset, expected, x); } /** * Fetches a reference value from a given Java variable, with volatile * load semantics. Otherwise identical to {@link #getObject(Object, long)} + * + * @deprecated Use {@link VarHandle#getVolatile(Object...)} instead. */ + @Deprecated(since="23", forRemoval=true) @ForceInline public Object getObjectVolatile(Object o, long offset) { + beforeMemoryAccess(); return theInternalUnsafe.getReferenceVolatile(o, offset); } /** * Stores a reference value into a given Java variable, with * volatile store semantics. Otherwise identical to {@link #putObject(Object, long, Object)} + * + * @deprecated Use {@link VarHandle#setVolatile(Object...)} instead. */ + @Deprecated(since="23", forRemoval=true) @ForceInline public void putObjectVolatile(Object o, long offset, Object x) { + beforeMemoryAccess(); theInternalUnsafe.putReferenceVolatile(o, offset, x); } - /** Volatile version of {@link #getInt(Object, long)} */ + /** Volatile version of {@link #getInt(Object, long)}. + * + * @deprecated Use {@link VarHandle#getVolatile(Object...)} instead. + */ + @Deprecated(since="23", forRemoval=true) @ForceInline public int getIntVolatile(Object o, long offset) { + beforeMemoryAccess(); return theInternalUnsafe.getIntVolatile(o, offset); } - /** Volatile version of {@link #putInt(Object, long, int)} */ + /** Volatile version of {@link #putInt(Object, long, int)}. + * + * @deprecated Use {@link VarHandle#setVolatile(Object...)} instead. + */ + @Deprecated(since="23", forRemoval=true) @ForceInline public void putIntVolatile(Object o, long offset, int x) { + beforeMemoryAccess(); theInternalUnsafe.putIntVolatile(o, offset, x); } - /** Volatile version of {@link #getBoolean(Object, long)} */ + /** Volatile version of {@link #getBoolean(Object, long)}. + * + * @deprecated Use {@link VarHandle#getVolatile(Object...)} instead. + */ + @Deprecated(since="23", forRemoval=true) @ForceInline public boolean getBooleanVolatile(Object o, long offset) { + beforeMemoryAccess(); return theInternalUnsafe.getBooleanVolatile(o, offset); } - /** Volatile version of {@link #putBoolean(Object, long, boolean)} */ + /** Volatile version of {@link #putBoolean(Object, long, boolean)}. + * + * @deprecated Use {@link VarHandle#setVolatile(Object...)} instead. + */ + @Deprecated(since="23", forRemoval=true) @ForceInline public void putBooleanVolatile(Object o, long offset, boolean x) { + beforeMemoryAccess(); theInternalUnsafe.putBooleanVolatile(o, offset, x); } - /** Volatile version of {@link #getByte(Object, long)} */ + /** Volatile version of {@link #getByte(Object, long)}. + * + * @deprecated Use {@link VarHandle#getVolatile(Object...)} + * instead. + */ + @Deprecated(since="23", forRemoval=true) @ForceInline public byte getByteVolatile(Object o, long offset) { + beforeMemoryAccess(); return theInternalUnsafe.getByteVolatile(o, offset); } - /** Volatile version of {@link #putByte(Object, long, byte)} */ + /** Volatile version of {@link #putByte(Object, long, byte)}. + * + * @deprecated Use {@link VarHandle#setVolatile(Object...)} instead. + */ + @Deprecated(since="23", forRemoval=true) @ForceInline public void putByteVolatile(Object o, long offset, byte x) { + beforeMemoryAccess(); theInternalUnsafe.putByteVolatile(o, offset, x); } - /** Volatile version of {@link #getShort(Object, long)} */ + /** Volatile version of {@link #getShort(Object, long)}. + * + * @deprecated Use {@link VarHandle#getVolatile(Object...)} instead. + */ + @Deprecated(since="23", forRemoval=true) @ForceInline public short getShortVolatile(Object o, long offset) { + beforeMemoryAccess(); return theInternalUnsafe.getShortVolatile(o, offset); } - /** Volatile version of {@link #putShort(Object, long, short)} */ + /** Volatile version of {@link #putShort(Object, long, short)}. + * + * @deprecated Use {@link VarHandle#setVolatile(Object...)} instead. + */ + @Deprecated(since="23", forRemoval=true) @ForceInline public void putShortVolatile(Object o, long offset, short x) { + beforeMemoryAccess(); theInternalUnsafe.putShortVolatile(o, offset, x); } - /** Volatile version of {@link #getChar(Object, long)} */ + /** Volatile version of {@link #getChar(Object, long)}. + * + * @deprecated Use {@link VarHandle#getVolatile(Object...)} instead. + */ + @Deprecated(since="23", forRemoval=true) @ForceInline public char getCharVolatile(Object o, long offset) { + beforeMemoryAccess(); return theInternalUnsafe.getCharVolatile(o, offset); } - /** Volatile version of {@link #putChar(Object, long, char)} */ + /** Volatile version of {@link #putChar(Object, long, char)}. + * + * @deprecated Use {@link VarHandle#setVolatile(Object...)} instead. + */ + @Deprecated(since="23", forRemoval=true) @ForceInline public void putCharVolatile(Object o, long offset, char x) { + beforeMemoryAccess(); theInternalUnsafe.putCharVolatile(o, offset, x); } - /** Volatile version of {@link #getLong(Object, long)} */ + /** Volatile version of {@link #getLong(Object, long)}. + * + * @deprecated Use {@link VarHandle#getVolatile(Object...)} instead. + */ + @Deprecated(since="23", forRemoval=true) @ForceInline public long getLongVolatile(Object o, long offset) { + beforeMemoryAccess(); return theInternalUnsafe.getLongVolatile(o, offset); } - /** Volatile version of {@link #putLong(Object, long, long)} */ + /** Volatile version of {@link #putLong(Object, long, long)}. + * + * @deprecated Use {@link VarHandle#setVolatile(Object...)} instead. + */ + @Deprecated(since="23", forRemoval=true) @ForceInline public void putLongVolatile(Object o, long offset, long x) { + beforeMemoryAccess(); theInternalUnsafe.putLongVolatile(o, offset, x); } - /** Volatile version of {@link #getFloat(Object, long)} */ + /** Volatile version of {@link #getFloat(Object, long)}. + * + * @deprecated Use {@link VarHandle#getVolatile(Object...)} instead. + */ + @Deprecated(since="23", forRemoval=true) @ForceInline public float getFloatVolatile(Object o, long offset) { + beforeMemoryAccess(); return theInternalUnsafe.getFloatVolatile(o, offset); } - /** Volatile version of {@link #putFloat(Object, long, float)} */ + /** Volatile version of {@link #putFloat(Object, long, float)}. + * + * @deprecated Use {@link VarHandle#setVolatile(Object...)} instead. + */ + @Deprecated(since="23", forRemoval=true) @ForceInline public void putFloatVolatile(Object o, long offset, float x) { + beforeMemoryAccess(); theInternalUnsafe.putFloatVolatile(o, offset, x); } - /** Volatile version of {@link #getDouble(Object, long)} */ + /** Volatile version of {@link #getDouble(Object, long)}. + * + * @deprecated Use {@link VarHandle#getVolatile(Object...)} instead. + */ + @Deprecated(since="23", forRemoval=true) @ForceInline public double getDoubleVolatile(Object o, long offset) { + beforeMemoryAccess(); return theInternalUnsafe.getDoubleVolatile(o, offset); } - /** Volatile version of {@link #putDouble(Object, long, double)} */ + /** Volatile version of {@link #putDouble(Object, long, double)}. + * + * @deprecated Use {@link VarHandle#setVolatile(Object...)} instead. + */ + @Deprecated(since="23", forRemoval=true) @ForceInline public void putDoubleVolatile(Object o, long offset, double x) { + beforeMemoryAccess(); theInternalUnsafe.putDoubleVolatile(o, offset, x); } @@ -1025,21 +1466,35 @@ public final class Unsafe { * that is otherwise only accessed using volatile accesses). * * Corresponds to C11 atomic_store_explicit(..., memory_order_release). + * + * @deprecated Use {@link VarHandle#setRelease(Object...)} instead. */ + @Deprecated(since="23", forRemoval=true) @ForceInline public void putOrderedObject(Object o, long offset, Object x) { + beforeMemoryAccess(); theInternalUnsafe.putReferenceRelease(o, offset, x); } - /** Ordered/Lazy version of {@link #putIntVolatile(Object, long, int)} */ + /** Ordered/Lazy version of {@link #putIntVolatile(Object, long, int)}. + * + * @deprecated Use {@link VarHandle#setRelease(Object...)} instead. + */ + @Deprecated(since="23", forRemoval=true) @ForceInline public void putOrderedInt(Object o, long offset, int x) { + beforeMemoryAccess(); theInternalUnsafe.putIntRelease(o, offset, x); } - /** Ordered/Lazy version of {@link #putLongVolatile(Object, long, long)} */ + /** Ordered/Lazy version of {@link #putLongVolatile(Object, long, long)}. + * + * @deprecated Use {@link VarHandle#setRelease(Object...)} instead. + */ + @Deprecated(since="23", forRemoval=true) @ForceInline public void putOrderedLong(Object o, long offset, long x) { + beforeMemoryAccess(); theInternalUnsafe.putLongRelease(o, offset, x); } @@ -1098,9 +1553,10 @@ public final class Unsafe { * @return the number of samples actually retrieved; or -1 * if the load average is unobtainable. * - * @deprecated Use {@link java.lang.management.OperatingSystemMXBean#getSystemLoadAverage()} + * @deprecated Use {@link java.management/java.lang.management.OperatingSystemMXBean#getSystemLoadAverage()} * instead. */ + @SuppressWarnings("doclint:reference") // cross-module links @Deprecated(since="22", forRemoval=true) @ForceInline public int getLoadAverage(double[] loadavg, int nelems) { @@ -1120,9 +1576,13 @@ public final class Unsafe { * @param delta the value to add * @return the previous value * @since 1.8 + * + * @deprecated Use {@link VarHandle#getAndAdd(Object...)} instead. */ + @Deprecated(since="23", forRemoval=true) @ForceInline public final int getAndAddInt(Object o, long offset, int delta) { + beforeMemoryAccess(); return theInternalUnsafe.getAndAddInt(o, offset, delta); } @@ -1136,9 +1596,13 @@ public final class Unsafe { * @param delta the value to add * @return the previous value * @since 1.8 + * + * @deprecated Use {@link VarHandle#getAndAdd(Object...)} instead. */ + @Deprecated(since="23", forRemoval=true) @ForceInline public final long getAndAddLong(Object o, long offset, long delta) { + beforeMemoryAccess(); return theInternalUnsafe.getAndAddLong(o, offset, delta); } @@ -1152,9 +1616,13 @@ public final class Unsafe { * @param newValue new value * @return the previous value * @since 1.8 + * + * @deprecated Use {@link VarHandle#getAndAdd(Object...)} instead. */ + @Deprecated(since="23", forRemoval=true) @ForceInline public final int getAndSetInt(Object o, long offset, int newValue) { + beforeMemoryAccess(); return theInternalUnsafe.getAndSetInt(o, offset, newValue); } @@ -1168,9 +1636,13 @@ public final class Unsafe { * @param newValue new value * @return the previous value * @since 1.8 + * + * @deprecated Use {@link VarHandle#getAndAdd(Object...)} instead. */ + @Deprecated(since="23", forRemoval=true) @ForceInline public final long getAndSetLong(Object o, long offset, long newValue) { + beforeMemoryAccess(); return theInternalUnsafe.getAndSetLong(o, offset, newValue); } @@ -1184,9 +1656,13 @@ public final class Unsafe { * @param newValue new value * @return the previous value * @since 1.8 + * + * @deprecated Use {@link VarHandle#getAndAdd(Object...)} instead. */ + @Deprecated(since="23", forRemoval=true) @ForceInline public final Object getAndSetObject(Object o, long offset, Object newValue) { + beforeMemoryAccess(); return theInternalUnsafe.getAndSetReference(o, offset, newValue); } @@ -1201,7 +1677,7 @@ public final class Unsafe { * is almost always desired, and most current hardware instructions that * provide a LoadLoad barrier also provide a LoadStore barrier for free. * - * @deprecated Use {@link java.lang.invoke.VarHandle#acquireFence()} instead. + * @deprecated Use {@link VarHandle#acquireFence()} instead. * @since 1.8 */ @Deprecated(since="22", forRemoval=true) @@ -1221,7 +1697,7 @@ public final class Unsafe { * is almost always desired, and most current hardware instructions that * provide a StoreStore barrier also provide a LoadStore barrier for free. * - * @deprecated Use {@link java.lang.invoke.VarHandle#releaseFence()} instead. + * @deprecated Use {@link VarHandle#releaseFence()} instead. * @since 1.8 */ @Deprecated(since="22", forRemoval=true) @@ -1238,7 +1714,7 @@ public final class Unsafe { * * Corresponds to C11 atomic_thread_fence(memory_order_seq_cst). * - * @deprecated Use {@link java.lang.invoke.VarHandle#fullFence()} instead. + * @deprecated Use {@link VarHandle#fullFence()} instead. * @since 1.8 */ @Deprecated(since="22", forRemoval=true) @@ -1255,12 +1731,207 @@ public final class Unsafe { * @throws IllegalArgumentException if {@code directBuffer} is non-direct, * or is a {@link java.nio.Buffer#slice slice}, or is a * {@link java.nio.Buffer#duplicate duplicate} + * + * @deprecated Use a {@link MemorySegment} allocated in an {@link Arena} with the + * appropriate temporal bounds. The {@link MemorySegment#asByteBuffer()} method + * wraps a memory segment as a {@code ByteBuffer} to allow interop with existing + * code. + * * @since 9 */ + @Deprecated(since="23", forRemoval=true) public void invokeCleaner(java.nio.ByteBuffer directBuffer) { if (!directBuffer.isDirect()) - throw new IllegalArgumentException("buffer is non-direct"); - + throw new IllegalArgumentException("Not a direct buffer"); + beforeMemoryAccess(); theInternalUnsafe.invokeCleaner(directBuffer); } + + // Infrastructure for --sun-misc-unsafe-memory-access= command line option. + + private static final Object MEMORY_ACCESS_WARNED_BASE; + private static final long MEMORY_ACCESS_WARNED_OFFSET; + static { + try { + Field field = Unsafe.class.getDeclaredField("memoryAccessWarned"); + MEMORY_ACCESS_WARNED_BASE = theInternalUnsafe.staticFieldBase(field); + MEMORY_ACCESS_WARNED_OFFSET = theInternalUnsafe.staticFieldOffset(field); + } catch (Exception e) { + throw new ExceptionInInitializerError(e); + } + } + // set to true by first usage of memory-access method + private static @Stable boolean memoryAccessWarned; + + private static boolean isMemoryAccessWarned() { + return theInternalUnsafe.getBooleanVolatile(MEMORY_ACCESS_WARNED_BASE, MEMORY_ACCESS_WARNED_OFFSET); + } + + private static boolean trySetMemoryAccessWarned() { + return theInternalUnsafe.compareAndSetBoolean(MEMORY_ACCESS_WARNED_BASE, MEMORY_ACCESS_WARNED_OFFSET, false, true); + } + + private static final MemoryAccessOption MEMORY_ACCESS_OPTION = MemoryAccessOption.value(); + + /** + * Invoked by all memory-access methods. + */ + @ForceInline + private static void beforeMemoryAccess() { + if (MEMORY_ACCESS_OPTION == MemoryAccessOption.ALLOW) { + return; + } + + if (MEMORY_ACCESS_OPTION == MemoryAccessOption.WARN && isMemoryAccessWarned()) { + // nothing to do if this is not the first usage + return; + } + + // warn && first usage, debug, or deny + beforeMemoryAccessSlow(); + } + + private static void beforeMemoryAccessSlow() { + assert MEMORY_ACCESS_OPTION != MemoryAccessOption.ALLOW; + + // stack trace without the frames for the beforeMemoryAccess methods + List stack = StackWalkerHolder.INSTANCE.walk(s -> + s.dropWhile(f -> (f.getDeclaringClass() == Unsafe.class) + && f.getMethodName().startsWith("beforeMemoryAccess")) + .limit(32) + .toList() + ); + + // callerClass -> Unsafe.methodName + String methodName = stack.get(0).getMethodName(); + Class callerClass = stack.get(1).getDeclaringClass(); + + switch (MEMORY_ACCESS_OPTION) { + case WARN -> { + if (trySetMemoryAccessWarned()) { + log(multiLineWarning(callerClass, methodName)); + } + } + case DEBUG -> { + String warning = singleLineWarning(callerClass, methodName); + StringBuilder sb = new StringBuilder(warning); + stack.stream() + .skip(1) + .forEach(f -> + sb.append(System.lineSeparator()).append("\tat " + f) + ); + log(sb.toString()); + } + case DENY -> { + throw new UnsupportedOperationException(methodName); + } + } + } + + /** + * Represents the options for the depreacted method-access methods. + */ + private enum MemoryAccessOption { + /** + * Allow use of the memory-access methods with no warnings. + */ + ALLOW, + /** + * Warning on the first use of a memory-access method. + */ + WARN, + /** + * One-line warning and a stack trace on every use of a memory-access method. + */ + DEBUG, + /** + * Deny use of the memory-access methods. + */ + DENY; + + private static MemoryAccessOption defaultValue() { + return ALLOW; + } + + /** + * Return the value. + */ + static MemoryAccessOption value() { + String value = VM.getSavedProperty("sun.misc.unsafe.memory.access"); + if (value != null) { + return switch (value) { + case "allow" -> MemoryAccessOption.ALLOW; + case "warn" -> MemoryAccessOption.WARN; + case "debug" -> MemoryAccessOption.DEBUG; + case "deny" -> MemoryAccessOption.DENY; + default -> { + // should not happen + log("sun.misc.unsafe.memory.access ignored, value '" + value + + "' is not a recognized value"); + yield defaultValue(); + } + }; + } else { + return defaultValue(); + } + } + } + + /** + * Holder for StackWalker that retains class references. + */ + private static class StackWalkerHolder { + static final StackWalker INSTANCE; + static { + PrivilegedAction pa = () -> + StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE); + @SuppressWarnings("removal") + StackWalker walker = AccessController.doPrivileged(pa); + INSTANCE = walker; + } + } + + /** + * Return the multi-line warning message for when the given class invokes the + * given the Unsafe method. + */ + private static String multiLineWarning(Class callerClass, String methodName) { + return String.format( + """ + WARNING: A terminally deprecated method in sun.misc.Unsafe has been called + WARNING: sun.misc.Unsafe::%s has been called by %s + WARNING: Please consider reporting this to the maintainers of %s + WARNING: sun.misc.Unsafe::%s will be removed in a future release""", + methodName, callerAndLocation(callerClass), callerClass, methodName); + } + + /** + * Return the single-line warning message for when the given class invokes the + * given the Unsafe method. + */ + private static String singleLineWarning(Class callerClass, String methodName) { + return String.format("WARNING: sun.misc.Unsafe::%s called by %s", + methodName, callerAndLocation(callerClass)); + } + + /** + * Returns a string with the caller class and the location URL from the CodeSource. + */ + private static String callerAndLocation(Class callerClass) { + PrivilegedAction pa = callerClass::getProtectionDomain; + @SuppressWarnings("removal") + CodeSource cs = AccessController.doPrivileged(pa).getCodeSource(); + String who = callerClass.getName(); + if (cs != null && cs.getLocation() != null) { + who += " (" + cs.getLocation() + ")"; + } + return who; + } + + /** + * Prints the given message to the standard error. + */ + private static void log(String message) { + VM.initialErr().println(message); + } } diff --git a/test/jdk/sun/misc/TryUnsafeMemoryAccess.java b/test/jdk/sun/misc/TryUnsafeMemoryAccess.java new file mode 100644 index 00000000000..497940eca9a --- /dev/null +++ b/test/jdk/sun/misc/TryUnsafeMemoryAccess.java @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.nio.ByteBuffer; +import sun.misc.Unsafe; + +/** + * Launched by UnsafeMemoryAccessWarnings with a '+' delimited list of methods to invoke. + */ +@SuppressWarnings("removal") +public class TryUnsafeMemoryAccess { + private static final Unsafe UNSAFE; + static { + try { + Field f = Unsafe.class.getDeclaredField("theUnsafe"); + f.setAccessible(true); + UNSAFE = (Unsafe) f.get(null); + } catch (Exception e) { + throw new ExceptionInInitializerError(e); + } + } + private static long address; + private static long offset; + + static class TestClass { + long value; + TestClass(long value) { + this.value = value; + } + } + + /** + * The argument is a list of names of no-arg static methods in this class to invoke. + * The names are separated with a '+'. + */ + public static void main(String[] args) throws Exception { + String[] methodNames = args[0].split("\\+"); + for (String methodName : methodNames) { + Method m = TryUnsafeMemoryAccess.class.getDeclaredMethod(methodName); + try { + m.invoke(null); + } catch (Throwable e) { + e.printStackTrace(); + } + } + } + + // a selection of Unsafe memory access methods to test + + static void allocateMemory() { + address = UNSAFE.allocateMemory(100); + } + + static void freeMemory() { + if (address == 0) + throw new RuntimeException("allocateMemory not called"); + UNSAFE.freeMemory(address); + } + + static void objectFieldOffset() throws Exception { + Field f = TestClass.class.getDeclaredField("value"); + offset = UNSAFE.objectFieldOffset(f); + } + + static void getLong() { + if (offset == 0) + throw new RuntimeException("objectFieldOffset not called"); + var obj = new TestClass(99); + long value = UNSAFE.getLong(obj, offset); + if (value != 99) { + throw new RuntimeException(); + } + } + + static void putLong() { + if (offset == 0) + throw new RuntimeException("objectFieldOffset not called"); + var obj = new TestClass(0); + UNSAFE.putLong(obj, offset, 99); + if (obj.value != 99) { + throw new RuntimeException(); + } + } + + static void invokeCleaner() { + var dbb = ByteBuffer.allocateDirect(1000); + UNSAFE.invokeCleaner(dbb); + } + + /** + * Invoke Unsafe.allocateMemory reflectively. + */ + static void reflectivelyAllocateMemory() throws Exception { + Method allocateMemory = Unsafe.class.getMethod("allocateMemory", long.class); + address = (long) allocateMemory.invoke(UNSAFE, 100); + } + + /** + * Invoke Unsafe.freeMemory reflectively. + */ + static void reflectivelyFreeMemory() throws Exception { + if (address == 0) + throw new RuntimeException("allocateMemory not called"); + Method freeMemory = Unsafe.class.getMethod("freeMemory", long.class); + freeMemory.invoke(UNSAFE, address); + } + + /** + * Used to test that the property value from startup is used. + */ + static void setSystemPropertyToAllow() { + System.setProperty("sun.misc.unsafe.memory.access", "allow"); + } +} diff --git a/test/jdk/sun/misc/UnsafeMemoryAccessWarnings.java b/test/jdk/sun/misc/UnsafeMemoryAccessWarnings.java new file mode 100644 index 00000000000..3d4b6965828 --- /dev/null +++ b/test/jdk/sun/misc/UnsafeMemoryAccessWarnings.java @@ -0,0 +1,212 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8331670 + * @summary Basic test for --sun-misc-unsafe-memory-access= + * @library /test/lib + * @compile TryUnsafeMemoryAccess.java + * @run junit UnsafeMemoryAccessWarnings + */ + +import java.util.stream.Stream; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; +import static org.junit.jupiter.api.Assertions.*; + +import jdk.test.lib.process.ProcessTools; +import jdk.test.lib.process.OutputAnalyzer; + +class UnsafeMemoryAccessWarnings { + + /** + * Test default is "allow" + */ + @Test + void testDefault() throws Exception { + test("allocateMemory+freeMemory+objectFieldOffset+putLong+getLong+invokeCleaner") + .shouldHaveExitValue(0) + .shouldNotContain("WARNING: A terminally deprecated method in sun.misc.Unsafe has been called") + .shouldNotContain("WARNING: sun.misc.Unsafe::allocateMemory") + .shouldNotContain("WARNING: sun.misc.Unsafe::freeMemory") + .shouldNotContain("WARNING: sun.misc.Unsafe::objectFieldOffset") + .shouldNotContain("WARNING: sun.misc.Unsafe::putLong") + .shouldNotContain("WARNING: sun.misc.Unsafe::getLong") + .shouldNotContain("WARNING: sun.misc.Unsafe::invokeCleaner"); + } + + /** + * Test --sun-misc-unsafe-memory-access=allow + */ + @Test + void testAllow() throws Exception { + test("allocateMemory+freeMemory+objectFieldOffset+putLong+getLong+invokeCleaner", + "--sun-misc-unsafe-memory-access=allow") + .shouldHaveExitValue(0) + .shouldNotContain("WARNING: A terminally deprecated method in sun.misc.Unsafe has been called") + .shouldNotContain("WARNING: sun.misc.Unsafe::allocateMemory") + .shouldNotContain("WARNING: sun.misc.Unsafe::freeMemory") + .shouldNotContain("WARNING: sun.misc.Unsafe::objectFieldOffset") + .shouldNotContain("WARNING: sun.misc.Unsafe::putLong") + .shouldNotContain("WARNING: sun.misc.Unsafe::getLong") + .shouldNotContain("WARNING: sun.misc.Unsafe::invokeCleaner"); + } + + /** + * Test --sun-misc-unsafe-memory-access=warn + */ + @ParameterizedTest + @ValueSource(strings = { + "allocateMemory+freeMemory", + "objectFieldOffset+putLong+getLong", + "invokeCleaner" + }) + void testWarn(String input) throws Exception { + var output = test(input, "--sun-misc-unsafe-memory-access=warn").shouldHaveExitValue(0); + + // should be warning printed for the first memory access method + String[] methodNames = input.split("\\+"); + String firstMethodName = methodNames[0]; + output.shouldContain("WARNING: A terminally deprecated method in sun.misc.Unsafe has been called") + .shouldContain("WARNING: sun.misc.Unsafe::" + firstMethodName + " has been called by") + .shouldContain("WARNING: Please consider reporting this to the maintainers of") + .shouldContain("WARNING: sun.misc.Unsafe::" + firstMethodName + " will be removed in a future release"); + + // should be no warning for the second/subsequent memory access methods + int index = 1; + while (index < methodNames.length) { + String methodName = methodNames[index++]; + output.shouldNotContain("WARNING: sun.misc.Unsafe::" + methodName); + } + } + + /** + * Test --sun-misc-unsafe-memory-access=debug + */ + @Test + void testDebug() throws Exception { + test("allocateMemory+freeMemory+objectFieldOffset+putLong+getLong+invokeCleaner", + "--sun-misc-unsafe-memory-access=debug") + .shouldHaveExitValue(0) + .shouldContain("WARNING: sun.misc.Unsafe::allocateMemory called") + .shouldContain("WARNING: sun.misc.Unsafe::freeMemory called") + .shouldContain("WARNING: sun.misc.Unsafe::objectFieldOffset called") + .shouldContain("WARNING: sun.misc.Unsafe::putLong called") + .shouldContain("WARNING: sun.misc.Unsafe::getLong called") + .shouldContain("WARNING: sun.misc.Unsafe::invokeCleaner called"); + } + + /** + * Test --sun-misc-unsafe-memory-access=deny + */ + @Test + void testDeny() throws Exception { + test("allocateMemory+objectFieldOffset+invokeCleaner", "--sun-misc-unsafe-memory-access=deny") + .shouldHaveExitValue(0) + .shouldContain("java.lang.UnsupportedOperationException: allocateMemory") + .shouldContain("java.lang.UnsupportedOperationException: objectFieldOffset") + .shouldContain("java.lang.UnsupportedOperationException: invokeCleaner"); + } + + /** + * Test invoking Unsafe methods with core reflection. + */ + @Test + void testInvokeReflectively() throws Exception { + test("reflectivelyAllocateMemory+reflectivelyFreeMemory", "--sun-misc-unsafe-memory-access=allow") + .shouldHaveExitValue(0) + .shouldNotContain("WARNING: A terminally deprecated method in sun.misc.Unsafe has been called") + .shouldNotContain("WARNING: sun.misc.Unsafe::allocateMemory") + .shouldNotContain("WARNING: sun.misc.Unsafe::freeMemory"); + + test("reflectivelyAllocateMemory+reflectivelyFreeMemory", "--sun-misc-unsafe-memory-access=warn") + .shouldHaveExitValue(0) + .shouldContain("WARNING: A terminally deprecated method in sun.misc.Unsafe has been called") + .shouldContain("WARNING: sun.misc.Unsafe::allocateMemory has been called by") + .shouldContain("WARNING: Please consider reporting this to the maintainers of") + .shouldContain("WARNING: sun.misc.Unsafe::allocateMemory will be removed in a future release") + .shouldNotContain("WARNING: sun.misc.Unsafe::freeMemory"); + + test("reflectivelyAllocateMemory+reflectivelyFreeMemory", "--sun-misc-unsafe-memory-access=debug") + .shouldHaveExitValue(0) + .shouldContain("WARNING: sun.misc.Unsafe::allocateMemory called") + .shouldContain("WARNING: sun.misc.Unsafe::freeMemory called"); + + test("reflectivelyAllocateMemory", "--sun-misc-unsafe-memory-access=deny") + .shouldHaveExitValue(0) + .shouldContain("java.lang.UnsupportedOperationException: allocateMemory"); + } + + /** + * If --sun-misc-unsafe-memory-access specified more than once then last one wins. + */ + @Test + void testLastOneWins() throws Exception { + test("allocateMemory+objectFieldOffset+invokeCleaner", + "--sun-misc-unsafe-memory-access=allow", + "--sun-misc-unsafe-memory-access=deny") + .shouldHaveExitValue(0) + .shouldContain("java.lang.UnsupportedOperationException: allocateMemory") + .shouldContain("java.lang.UnsupportedOperationException: objectFieldOffset") + .shouldContain("java.lang.UnsupportedOperationException: invokeCleaner"); + } + + /** + * Test --sun-misc-unsafe-memory-access with invalid values. + */ + @ParameterizedTest + @ValueSource(strings = { "", "bad" }) + void testInvalidValues(String value) throws Exception { + test("allocateMemory", "--sun-misc-unsafe-memory-access=" + value) + .shouldNotHaveExitValue(0) + .shouldContain("Value specified to --sun-misc-unsafe-memory-access not recognized: '" + value); + } + + /** + * Test System.setProperty("sun.misc.unsafe.memory.access", "allow") + * The saved value from startup should be used, not the system property set at run-time. + */ + @Test + void testSetPropertyToAllow() throws Exception { + test("setSystemPropertyToAllow+objectFieldOffset", "--sun-misc-unsafe-memory-access=deny") + .shouldHaveExitValue(0) + .shouldContain("java.lang.UnsupportedOperationException: objectFieldOffset"); + } + + /** + * Launch TryUnsafeMemoryAccess with the given arguments and VM options. + */ + private OutputAnalyzer test(String action, String... vmopts) throws Exception { + Stream s1 = Stream.of(vmopts); + Stream s2 = Stream.of("TryUnsafeMemoryAccess", action); + String[] opts = Stream.concat(s1, s2).toArray(String[]::new); + var outputAnalyzer = ProcessTools + .executeTestJava(opts) + .outputTo(System.err) + .errorTo(System.err); + return outputAnalyzer; + } +} diff --git a/test/micro/org/openjdk/bench/sun/misc/UnsafeOps.java b/test/micro/org/openjdk/bench/sun/misc/UnsafeOps.java new file mode 100644 index 00000000000..1d4a80e43dd --- /dev/null +++ b/test/micro/org/openjdk/bench/sun/misc/UnsafeOps.java @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.bench.sun.misc; + +import java.lang.reflect.Field; +import java.util.concurrent.TimeUnit; +import sun.misc.Unsafe; +import org.openjdk.jmh.annotations.*; + +@BenchmarkMode(Mode.AverageTime) +@Warmup(iterations = 5, time = 500, timeUnit = TimeUnit.MILLISECONDS) +@Measurement(iterations = 10, time = 500, timeUnit = TimeUnit.MILLISECONDS) +@State(org.openjdk.jmh.annotations.Scope.Thread) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +@SuppressWarnings("removal") +public class UnsafeOps { + static final Unsafe U; + static { + try { + Field f = Unsafe.class.getDeclaredField("theUnsafe"); + f.setAccessible(true); + U = (Unsafe) f.get(null); + } catch (ReflectiveOperationException ex) { + throw new IllegalStateException(); + } + } + + private static class TestClass { + long value; + } + + private Object object; + private long valueOffset; + private long address; + + @Setup + public void setup() throws Exception { + object = new TestClass(); + Field f = TestClass.class.getDeclaredField("value"); + valueOffset = U.objectFieldOffset(f); + + address = U.allocateMemory(1000); + } + + @TearDown + public void finish() { + U.freeMemory(address); + } + + @Benchmark + public void putLongOnHeap() { + U.putLong(object, 0, 99); + } + + @Benchmark + public long getLongOnHeap() { + return U.getLong(object, 0); + } + + @Benchmark + public void putLongOffHeap() { + U.putLong(null, address, 99); + } + + @Benchmark + public long getLongOffHeap() { + return U.getLong(null, address); + } +}