This document discusses various approaches to accessing the sun.misc.Unsafe class from outside of the JDK/JRE, as it is an internal class not intended for public use. It presents several options for retrieving an Unsafe instance, such as directly calling Unsafe.getUnsafe() (which only works inside JDK/JRE), accessing the "theUnsafe" field via reflection, or constructing a new Unsafe instance using a private constructor. However, it notes that none of these options feel quite right as sun.misc.Unsafe is an internal class, and its use is discouraged outside of the JDK/JRE.
11. www.hazelcast.com@noctarius2k
Used inside and outside the JDK / JRE
custom (64-bit)
memory allocation
fast
memory access
fast
(de)serialization
minimizegc overhead
custom
memory fences
efficientmemory layout
(array element)
volatile semantics
12. www.hazelcast.com@noctarius2k
Used inside and outside the JDK / JRE
// Unsafe mechanics
private static final sun.misc.Unsafe U;
private static final long QBASE;
private static final long QLOCK;
private static final int ABASE;
private static final int ASHIFT;
static {
try {
U = sun.misc.Unsafe.getUnsafe();
Class<?> k = WorkQueue.class;
Class<?> ak = ForkJoinTask[].class;
QBASE = U.objectFieldOffset
(k.getDeclaredField("base"));
QLOCK = U.objectFieldOffset
(k.getDeclaredField("qlock"));
ABASE = U.arrayBaseOffset(ak);
int scale = U.arrayIndexScale(ak);
if ((scale & (scale - 1)) != 0)
throw new Error("data type scale not a power of two");
ASHIFT = 31 - Integer.numberOfLeadingZeros(scale);
} catch (Exception e) {
throw new Error(e);
}
}
}
example:
java.util.concurrent.ForkJoinPool
17. www.hazelcast.com@noctarius2k
#
# A fatal error has been detected by the Java Runtime Environment:
#
# SIGSEGV (0xb) at pc=0x00000001067314b5, pid=25244, tid=5891
#
# JRE version: Java(TM) SE Runtime Environment (8.0_05-b13) (build 1.8.0_05-b13)
# Java VM: Java HotSpot(TM) 64-Bit Server VM (25.5-b02 mixed mode bsd-amd64 compressed oops)
# Problematic frame:
# V [libjvm.dylib+0x5314b5] Unsafe_SetNativeAddress+0x36
#
# An error report file with more information is saved as:
# /Users/noctarius/git/hazelcast-only.idea/hs_err_pid25244.log
#
# If you would like to submit a bug report, please visit:
# http://bugreport.sun.com/bugreport/crash.jsp
#
30. www.hazelcast.com@noctarius2k
Close ;-)
Language URL sun.misc.Unsafe
Avian C++ https://github.com/ReadyTalk/avian Yes
BicaVM JavaScript https://github.com/nurv/BicaVM No
Cacao C http://www.cacaojvm.org Yes
Excelsior JVM C / C++ ? http://www.excelsiorjet.com Yes
Dalvik / ART / Android C / C++ https://source.android.com/source/index.html Yes
Graal mainly Java http://openjdk.java.net/projects/graal Yes
GCJ C / C++ https://gcc.gnu.org/java Yes
HaikuVM C http://haiku-vm.sourceforge.net No
IBM J9 C / C++ ? http://www.ibm.com/developerworks/java/jdk/index.html Yes
IcedTea C / C++ http://icedtea.classpath.org/wiki/Main_Page Yes
IKVM.NET C# http://www.ikvm.net Yes
Jamiga C https://sourceforge.net/projects/jamiga2 No
JamVM C http://jamvm.sourceforge.net Yes
JatoVM C http://jatovm.org Yes
JikesRVM C microkernel, Java http://www.jikesrvm.org Yes
JNode C microkernel, Java http://www.jnode.org Yes
JVM.go Go https://github.com/zxh0/jvm.go Yes
JX C http://www4.cs.fau.de/Projects/JX/index.html No
Kaffe C https://github.com/kaffe/kaffe Yes
NanoVM C http://harbaum.org/till/nanovm/index.shtml No
OpenJDK / Oracle / Zulu C / C++ http://openjdk.java.net Yes
RoboVM C / C++ / ObjC https://robovm.com Yes
SAPJVM C / C++ ? http://help.sap.com/saphelp_nwce10/helpdata/en/47/
dc90b4ef17452289f9128b8c2bbd77/content.htm
Yes
Waratek Multitenant JVM C / C++ ? http://www.waratek.com Yes
Zing JVM C / C++ ? https://www.azul.com/products/zing Yes
33. www.hazelcast.com@noctarius2k
http://mail.openjdk.java.net/pipermail/openjfx-dev/2015-April/017028.html
Let me be blunt -- sun.misc.Unsafe must die in a fire. It is -- wait
for it -- Unsafe. It must go. Ignore any kind of theoretical rope and
start the path to righteousness _*/now/*_. It is still years until the
end of public updates to JDK 8, so we have /*years */to work this out
properly. But sticking our heads in the collective sands and hoping for
trivial work arounds to Unsafe is not going to work. If you're using
Unsafe, this is the year to explain where the API is broken and get it
straight....
Please help us kill Unsafe, kill Unsafe dead, kill Unsafe right, and do
so as quickly as possible to the ultimate benefit of everyone.
Wed Apr 8 19:15:42 UTC 2015, OpenJFX Mailinglist, Donald Smith, Oracle
34. www.hazelcast.com@noctarius2k
http://mail.openjdk.java.net/pipermail/openjfx-dev/2015-April/017028.html
Let me be blunt -- sun.misc.Unsafe must die in a fire. It is -- wait
for it -- Unsafe. It must go. Ignore any kind of theoretical rope and
start the path to righteousness _*/now/*_. It is still years until the
end of public updates to JDK 8, so we have /*years */to work this out
properly. But sticking our heads in the collective sands and hoping for
trivial work arounds to Unsafe is not going to work. If you're using
Unsafe, this is the year to explain where the API is broken and get it
straight....
Please help us kill Unsafe, kill Unsafe dead, kill Unsafe right, and do
so as quickly as possible to the ultimate benefit of everyone.
Wed Apr 8 19:15:42 UTC 2015, OpenJFX Mailinglist, Donald Smith, Oracle
37. www.hazelcast.com@noctarius2k
http://mail.openjdk.java.net/pipermail/openjfx-dev/2015-April/017028.html
Let me be blunt -- sun.misc.Unsafe must die in a fire. It is -- wait
for it -- Unsafe. It must go. Ignore any kind of theoretical rope and
start the path to righteousness _*/now/*_. It is still years until the
end of public updates to JDK 8, so we have /*years */to work this out
properly. But sticking our heads in the collective sands and hoping for
trivial work arounds to Unsafe is not going to work. If you're using
Unsafe, this is the year to explain where the API is broken and get it
straight....
Please help us kill Unsafe, kill Unsafe dead, kill Unsafe right, and do
so as quickly as possible to the ultimate benefit of everyone.
Wed Apr 8 19:15:42 UTC 2015, OpenJFX Mailinglist, Donald Smith, Oracle
46. www.hazelcast.com@noctarius2k
Problem 1: Get Access
private static final Unsafe UNSAFE;
static {
try {
Field theUnsafeField = Unsafe.class.getDeclaredField("theUnsafe");
theUnsafeField.setAccessible(true);
UNSAFE = (Unsafe) theUnsafeField.get(Unsafe.class);
} catch (Exception e) {
// We need you! =(
throw new Error(e);
}
}
The Ugly One
47. www.hazelcast.com@noctarius2k
Problem 1: Get Accessprivate static Unsafe findUnsafe() {
try {
return Unsafe.getUnsafe();
} catch (SecurityException se) {
return AccessController.doPrivileged(new PrivilegedAction<Unsafe>() {
@Override
public Unsafe run() {
try {
Class<Unsafe> type = Unsafe.class;
try {
Field field = type.getDeclaredField("theUnsafe");
field.setAccessible(true);
return type.cast(field.get(type));
} catch (Exception e) {
for (Field field : type.getDeclaredFields()) {
if (type.isAssignableFrom(field.getType())) {
field.setAccessible(true);
return type.cast(field.get(type));
}
}
}
} catch (Exception e) {
throw new RuntimeException("Unsafe unavailable", e);
}
throw new RuntimeException("Unsafe unavailable");
}
});
}
}
The Hazelcast One
(also working in most other JVMs)
48. www.hazelcast.com@noctarius2k
Problem 1: Get Access
The Tricky One
private static final Unsafe UNSAFE;
static {
try {
Constructor<Unsafe> constructor =
Unsafe.class.getDeclaredConstructor();
constructor.setAccessible(true);
UNSAFE = constructor.newInstance();
} catch (Exception e) {
// We need you! =(
throw new Error(e);
}
}
49. www.hazelcast.com@noctarius2k
Problem 1: Get Access
The Tricky One
private static final Unsafe UNSAFE;
static {
try {
Constructor<Unsafe> constructor =
Unsafe.class.getDeclaredConstructor();
constructor.setAccessible(true);
UNSAFE = constructor.newInstance();
} catch (Exception e) {
// We need you! =(
throw new Error(e);
}
}
55. www.hazelcast.com@noctarius2k
Problem 2: Atomic Updates
private static final Unsafe UNSAFE = findUnsafe();
private static long VERSION_OFFSET = findVersionOffset();
// Updated through Unsafe only!
private volatile long version = 0;
public long increment() {
while(true) {
long version = this.version;
long newVersion = version + 1;
if (UNSAFE.compareAndSwapLong(
this, VERSION_OFFSET, version, newVersion)) {
return newVersion;
}
}
}
Going Unsafe
57. www.hazelcast.com@noctarius2k
Problem 2: Atomic Updates
private final AtomicLong version = new AtomicLong(0);
public long increment() {
return version.incrementAndGet();
}
We can do better! There’s an API already!
But one AtomicLong instance per version
59. www.hazelcast.com@noctarius2k
Problem 2: Atomic Updates
We can still do better! There’s yet another API already!
private static final AtomicLongFieldUpdater<Record> VERSION =
AtomicLongFieldUpdater.newUpdater(Record, "version");
// Updated through atomic field updater only!
private volatile long version = 0;
public long increment() {
return VERSION.incrementAndGet(this);
}
60. www.hazelcast.com@noctarius2k
Solution 2: Atomic Updates
We can still do better! There’s yet another API already!
private static final AtomicLongFieldUpdater<Record> VERSION =
AtomicLongFieldUpdater.newUpdater(Record, "version");
// Updated through atomic field updater only!
private volatile long version = 0;
public long increment() {
return VERSION.incrementAndGet(this);
}
You guessed it, __ uses it ;-)
61. www.hazelcast.com@noctarius2k
Solution 2: Atomic Updates
private static final VarHandle VERSION = findVersionVarHandle();
// Updated through VarHandle only!
private volatile long version = 0;
public long increment() {
return (long) VERSION.addAndGet(this, 1);
}
JVM can inline those calls
63. www.hazelcast.com@noctarius2k
Problem 3: Memory Management
public long memory() {
long address = UNSAFE.allocateMemory(8);
UNSAFE.putLong(address, Long.MAX_VALUE);
return UNSAFE.getLong(address);
}
Keyword: Off-Heap
64. www.hazelcast.com@noctarius2k
Problem 3: Memory Management
ByteBuffer possible; API is 32bit though
public long memory() {
ByteBuffer byteBuffer = ByteBuffer.allocateDirect(8);
byteBuffer.putLong(0, Long.MAX_VALUE);
return byteBuffer.getLong(0);
}
70. www.hazelcast.com@noctarius2k
Problem 5: Native Interoperability
extern c {
JNIEXPORT int JNICALL
Java_ProcessIdentifier_getProcessId(JNIEnv *, jobject);
}
JNIEXPORT int JNICALL
Java_ProcessIdentifier_getProcessId(JNIEnv *env, jobject thisObj) {
return getpid();
}
public class ProcessIdentifier {
static {
System.loadLibrary("processidentifier");
}
public native void getProcessId();
}
71. www.hazelcast.com@noctarius2k
Problem 5: Native Interoperability
extern c {
JNIEXPORT int JNICALL
Java_ProcessIdentifier_getProcessId(JNIEnv *, jobject);
}
JNIEXPORT int JNICALL
Java_ProcessIdentifier_getProcessId(JNIEnv *env, jobject thisObj) {
return getpid();
}
public class ProcessIdentifier {
static {
System.loadLibrary("processidentifier");
}
public native void getProcessId();
}
Fortunately we don’t deal with it, but who loves it anyways ;-)
72. www.hazelcast.com@noctarius2k
Solution 5: Native Interoperability (for now)
interface LibC {
void getpid();
}
public int call() {
LibC c = LibraryLoader.create(LibC.class).load("c");
return c.getpid();
}
JNR, but guess what it uses internally ;-)
73. www.hazelcast.com@noctarius2k
Solution 5: Native Interoperability
public void call() {
MethodType intType = MethodType.methodType(int.class);
MethodHandle handle = MethodHandles
.findNative(null, “getpid”, intType);
return (int) handle.invokeExact();
}
They inline … just fine.
76. www.hazelcast.com@noctarius2k
Solution 6: Immutable Types
int[] values = new int[2];
int x = values[0];
int y = values[1];
Looks like
value class Point {
final int x;
final int y;
}
// Create a Point instance
Point point = makeValue(1, 2);
Value Types
77. www.hazelcast.com@noctarius2k
Solution 6: Immutable Types
value class Point {
final int x;
final int y;
}
// Create a Point instance
Point point = makeValue(1, 2);
Value Types
int[] values = new int[2];
int x = values[0];
int y = values[1];
Stack
var X
var Y
Looks like
Stack Allocation!
93. www.hazelcast.com@noctarius2k
Cleaner API
public class AutoCleaning implements AutoCloseable {
// A cleaner, preferably one shared within a library
private static final Cleaner cleaner = ...;
static class State
implements Runnable {
State(...) {/* initialize State needed for cleaning action */}
public void run() {/*cleanup action accessing State, executed at most once*/}
}
private final State state;
private final Cleaner.Cleanable cleanable;
public AutoCleaning() {
this.state = new State(...);
this.cleanable = cleaner.register(this, state);
}
public void close() {
cleanable.clean();
}
}