The announcement that sun.misc.Unsafe will disappear in Java 9 created a huge buzz in the community and lots of people didnt agree with the idea. While Unsafe have to go away eventually the time in not yet and Oracle finally agreed on that. To replace sun.misc.Unsafe we need a clear set of APIs to achieve the same behavior and performance. VarHandle, Foreign Function Calls, Generic Specialization (Templates) and other proposals try to solve this misery and to replace certain aspects of our beloved class. This talk will introduce the alternatives and explain how and why they will help to replace sun.misc.Unsafe. It uses basic examples to demonstrate what a Post-Apocalyptic Unsafe World will look like.
9. sun.misc.Unsafe - What you (don’t) know
• Internal class (sun.misc Package)
• Used inside the JVM / JRE
10. sun.misc.Unsafe - What you (don’t) know
• Internal class (sun.misc Package)
• Used inside the JVM / 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
11. sun.misc.Unsafe - What you (don’t) know
• Internal class (sun.misc Package)
• Used inside the JVM / JRE
• Used outside as well
12. sun.misc.Unsafe - What you (don’t) know
• Internal class (sun.misc Package)
• Used inside the JVM / JRE
• Used outside as well (good
// Deserialization
public Object readObject(InputStream is) {
SomeType element = unsafe.allocateInstance(SomeType.class);
element.deserializeValues(is);
return element;
}
13. sun.misc.Unsafe - What you (don’t) know
• Internal class (sun.misc Package)
• Used inside the JVM / JRE
• Used outside as well (good, bad)
14.
15.
16.
17. #
# 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
#
18. sun.misc.Unsafe - What you (don’t) know
• Internal class (sun.misc Package)
• Used inside the JVM / JRE
• Used outside as well (good / bad)
• Never intended for outside use
19. sun.misc.Unsafe - What you (don’t) know
• Internal class (sun.misc Package)
• Used inside the JVM / JRE
• Used outside as well (good / bad)
• Never intended for outside use
20. sun.misc.Unsafe - What you (don’t) know
• Internal class (sun.misc Package)
• Used inside the JVM / JRE
• Used outside as well (good / bad)
• Never intended for outside use
• Used by popular:
• Applications
• Frameworks
• Libraries
25. 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
27. Use Cases - The Good Ones!
• Low Latency, GC Overhead
28. Use Cases - The Good Ones!
• Low Latency, GC Overhead
Caution: Overly simplified!
Unsafe unsafe = findUnsafe();
long baseAddress = unsafe.allocateMemory(8);
class Point {
long offsetX = 0;
long offsetY = 4;
int getX(long baseAddress) {
return unsafe.getInt(baseAddress + offsetX);
}
int getY(long baseAddress) {
return unsafe.getInt(baseAddress + offsetY);
}
}
29. Use Cases - The Good Ones!
• Low Latency, GC Overhead
• Fast (de-)serialization
30. Use Cases - The Good Ones!
• Low Latency, GC Overhead
• Fast (de-)serialization
// Deserialization
public Object readObject(InputStream is) {
SomeType element = unsafe.allocateInstance(SomeType.class);
element.deserializeValues(is);
return element;
}
31. Use Cases - The Good Ones!
• Low Latency, GC Overhead
• Fast (de-)serialization
• 64bit Memory regions
32. Use Cases - The Good Ones!
• Low Latency, GC Overhead
• Fast (de-)serialization
• 64bit Memory regions
long baseAddress = unsafe.allocateMemory(Long.MAX_VALUE);
unsafe.putInt(baseAddress + Long.MAX_VALUE - 4, Integer.MAX_VALUE);
33. Use Cases - The Good Ones!
• Low Latency, GC Overhead
• Fast (de-)serialization
• 64bit Memory regions
• Atomic memory operations
34. Use Cases - The Good Ones!
• Low Latency, GC Overhead
• Fast (de-)serialization
• 64bit Memory regions
• Atomic memory operations
unsafe.compareAndSwapInt(this, offsetField, oldValue, newValue);
unsafe.compareAndSwapLong(this, offsetField, oldValue, newValue);
unsafe.compareAndSwapObject(this, offsetField, oldObject, newObject);
35. Use Cases - The Good Ones!
• Low Latency, GC Overhead
• Fast (de-)serialization
• 64bit Memory regions
• Atomic memory operations
• Efficient memory layout
36. Use Cases - The Good Ones!
• Low Latency, GC Overhead
• Fast (de-)serialization
• 64bit Memory regions
• Atomic memory operations
• Efficient memory layout
class Point {
long offsetX = 0;
long offsetY = 4;
}
offsetX
offsetY
37. Use Cases - The Good Ones!
• Low Latency, GC Overhead
• Fast (de-)serialization
• 64bit Memory regions
• Atomic memory operations
• Efficient memory layout
• Fast field / memory access
38. Use Cases - The Good Ones!
• Low Latency, GC Overhead
• Fast (de-)serialization
• 64bit Memory regions
• Atomic memory operations
• Efficient memory layout
• Fast field / memory access
long versionFieldOffset = unsafe.objectFieldOffset(versionField);
long version = unsafe.getLong(this, versionFieldOffset);
unsafe.putLong(this, versionFieldOffset, 10L);
39. Use Cases - The Good Ones!
• Low Latency, GC Overhead
• Fast (de-)serialization
• 64bit Memory regions
• Atomic memory operations
• Efficient memory layout
• Fast field / memory access
• Access to array elements with volatile semantics
40. Use Cases - The Good Ones!
• Low Latency, GC Overhead
• Fast (de-)serialization
• 64bit Memory regions
• Atomic memory operations
• Efficient memory layout
• Fast field / memory access
• Access to array elements with volatile semantics
long base = unsafe.arrayBaseOffset(int[].class);
long index = unsafe.arrayIndexScale(int[].class);
unsafe.getIntVolatile(array, base + (index * 3));
41. Use Cases - The Good Ones!
• Low Latency, GC Overhead
• Fast (de-)serialization
• 64bit Memory regions
• Atomic memory operations
• Efficient memory layout
• Fast field / memory access
• Access to array elements with volatile semantics
• Custom memory fences
42. Use Cases - The Good Ones!
• Low Latency, GC Overhead
• Fast (de-)serialization
• 64bit Memory regions
• Atomic memory operations
• Efficient memory layout
• Fast field / memory access
• Access to array elements with volatile semantics
• Custom memory fences
unsafe.fullFence();
unsafe.storeFence();
unsafe.loadFence();
43. Use Cases - The Good Ones!
• Low Latency, GC Overhead
• Fast (de-)serialization
• 64bit Memory regions
• Atomic memory operations
• Efficient memory layout
• Fast field / memory access
• Access to array elements with volatile semantics
• Custom memory fences
• Fast interaction with native code
44. Use Cases - The Good Ones!
• Low Latency, GC Overhead
• Fast (de-)serialization
• 64bit Memory regions
• Atomic memory operations
• Efficient memory layout
• Fast field / memory access
• Access to array elements with volatile semantics
• Custom memory fences
• Fast interaction with native code
long addr = unsafe.allocateMemory(50);
copyBytes(addr, byteArray);
nativeCall(addr);
46. Get Access
Easy One (not working outside the JRE / JDK)
Unsafe unsafe = Unsafe.getUnsafe();
47. Get Access
The Ugly One
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);
}
}
48. Get Access
The Working One
(at least for more than just HotSpot)
private 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");
}
});
}
}
49. 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);
}
}
53. 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
http://mail.openjdk.java.net/pipermail/openjfx-dev/2015-April/017028.html
54. 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
http://mail.openjdk.java.net/pipermail/openjfx-dev/2015-April/017028.html
55. 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
http://mail.openjdk.java.net/pipermail/openjfx-dev/2015-April/017028.html
56. 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
http://mail.openjdk.java.net/pipermail/openjfx-dev/2015-April/017028.html
58. Oracle created a new Taskforce
K.U.T.T. - Kill Unsafe To Total death
59. 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
http://mail.openjdk.java.net/pipermail/openjfx-dev/2015-April/017028.html
61. 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;
}
}
}
Atomic Updates
62. Atomic Updates
private final AtomicLong version = new AtomicLong(0);
public long increment() {
return version.incrementAndGet();
}
63. Atomic Updates
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);
}
64. 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);
}
87. Arrays 2.0
int[] values = new int[Long.MAX_VALUE];
Arrays.chop(T[] a, int newlength);
Box<String>[] ... = new Box<String>[];
88. Arrays 2.0
int[] values = new int[Long.MAX_VALUE];
Arrays.chop(T[] a, int newlength);
Box<int>[] ... = new Box<int>[];
89. Arrays 2.0
int[] values = new int[Long.MAX_VALUE];
Arrays.chop(T[] a, int newlength);
Box<int>[] ... = new Box<int>[];
char[] frozen = new char[20L].freeze();
90. Arrays 2.0
int[] values = new int[Long.MAX_VALUE];
Arrays.chop(T[] a, int newlength);
Box<int>[] ... = new Box<int>[];
char[] frozen = new char[20L].freeze();
Point[] points = new Point[3]; Stack
var X
var Y
var X
var Y
var X
var Y
points[0]
points[1]
points[2]
94. 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();
}
}
106. SpinWait Hints
Hints the JVM that a busy spin’s upcoming
JVM CAN intrinsify to use a WAIT opcode for the specific platform / CPU
107. SpinWait Hints
Hints the JVM that a busy spin’s upcoming
JVM CAN intrinsify to use a WAIT opcode for the specific platform / CPU
No-Op implementation for non supported CPUs
108. SpinWait Hints
Hints the JVM that a busy spin’s upcoming
JVM CAN intrinsify to use a WAIT opcode for the specific platform / CPU
No-Op implementation for non supported CPUs
while (!shutdown) {
while (queue.length == 0) {
// Hint the JVM to intrinsify wait
// opcode otherwise busy spin
Thread.onSpinWait();
}
consume(queue);
}
110. What Else?
No idea!
A lot is changing now.
Java / JVM will be different.
Don’t forget about Jigsaw,
test your application with Java 9 EA builds!
https://jdk9.java.net/jigsaw/