a post-apocalyptic sun.misc.unsafe world by christoph engelbert
TRANSCRIPT
http://www.superbwallpapers.com/fantasy/post-apocalyptic-tower-bridge-london-26546/
A Post-Apocalypticsun.misc.Unsafe
World
www.hazelcast.com@noctarius2k
Who’s that dude?• Chris Engelbert • Manager Developer Relations @Hazelcast
• Twitter-Senpai • Java-Passionist (10+ years)
• Performance • Garbage Collection • Benchmark Fairytales
sun.misc.Unsafe - What you (don’t) know
• Internal class (sun.misc Package)• Used inside the JVM / JRE
sun.misc.Unsafe - What you (don’t) know
• Internal class (sun.misc Package)• Used inside the JVM / JRE
// Unsafe mechanicsprivate 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
sun.misc.Unsafe - What you (don’t) know
• Internal class (sun.misc Package)• Used inside the JVM / JRE• Used outside as well
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;}
sun.misc.Unsafe - What you (don’t) know
• Internal class (sun.misc Package)• Used inside the JVM / JRE• Used outside as well (good, bad)
## 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#
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
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
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
MapDB
NettyHazelcast
Cassandra
Mockito
EasyMockJMock
Scala
Spock
Robolectric
Grails
GsonNeo4j
Apache IgniteApache Spark
Apache Kafka
Apache Wink
Apache Storm
Apache FlinkApache Continuum
Zookeeper
Dropwizard
Metrics (AOP)
Kryo
Byte Buddy
Hibernate
Liquibase
Spring Framework
Ehcache
OrientDB
Chronicle
Apache Hadoop
Apache HBase
GWTDisruptor
JRuby
AkkaAgrona
Aeron Simple Binary Encoding
XRebel
Presto
jol
ActivePivot
LWJGL
XAP
XStream
CapLogic
WildFly
Infinispan
MapDB
NettyHazelcast
Cassandra
Mockito
EasyMockJMock
Scala
Spock
Robolectric
Grails
GsonNeo4j
Apache IgniteApache Spark
Apache Kafka
Apache Wink
Apache Storm
Apache FlinkApache Continuum
Zookeeper
Dropwizard
Metrics (AOP)
Kryo
Byte Buddy
Hibernate
Liquibase
Spring Framework
Ehcache
OrientDB
Chronicle
Apache Hadoop
Apache HBase
GWTDisruptor
JRuby
AkkaAgrona
Aeron Simple Binary Encoding
XRebel
Presto
jol
ActivePivot
LWJGL
XAP
XStream
CapLogic
WildFly
Infinispan
Language URL sun.misc.UnsafeAvian C++ https://github.com/ReadyTalk/avian Yes
BicaVM JavaScript https://github.com/nurv/BicaVM NoCacao C http://www.cacaojvm.org Yes
Excelsior JVM C / C++ ? http://www.excelsiorjet.com YesDalvik / ART / Android C / C++ https://source.android.com/source/index.html Yes
Graal mainly Java http://openjdk.java.net/projects/graal YesGCJ C / C++ https://gcc.gnu.org/java Yes
HaikuVM C http://haiku-vm.sourceforge.net NoIBM J9 C / C++ ? http://www.ibm.com/developerworks/java/jdk/index.html YesIcedTea C / C++ http://icedtea.classpath.org/wiki/Main_Page Yes
IKVM.NET C# http://www.ikvm.net YesJamiga C https://sourceforge.net/projects/jamiga2 NoJamVM C http://jamvm.sourceforge.net YesJatoVM C http://jatovm.org Yes
JikesRVM C microkernel, Java http://www.jikesrvm.org YesJNode C microkernel, Java http://www.jnode.org YesJVM.go Go https://github.com/zxh0/jvm.go Yes
JX C http://www4.cs.fau.de/Projects/JX/index.html NoKaffe C https://github.com/kaffe/kaffe Yes
NanoVM C http://harbaum.org/till/nanovm/index.shtml NoOpenJDK / Oracle / Zulu C / C++ http://openjdk.java.net Yes
RoboVM C / C++ / ObjC https://robovm.com YesSAPJVM C / C++ ? http://help.sap.com/saphelp_nwce10/helpdata/en/47/
dc90b4ef17452289f9128b8c2bbd77/content.htmYes
Waratek Multitenant JVM C / C++ ? http://www.waratek.com YesZing JVM C / C++ ? https://www.azul.com/products/zing Yes
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); } }
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;}
Use Cases - The Good Ones!
• Low Latency, GC Overhead• Fast (de-)serialization• 64bit Memory regions
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);
Use Cases - The Good Ones!
• Low Latency, GC Overhead• Fast (de-)serialization• 64bit Memory regions• Atomic memory operations
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);
Use Cases - The Good Ones!
• Low Latency, GC Overhead• Fast (de-)serialization• 64bit Memory regions• Atomic memory operations• Efficient memory layout
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
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
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);
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
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));
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
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();
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
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);
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); }}
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"); } }); }}
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); }}
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
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
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
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
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
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
Atomic Updates
private final AtomicLong version = new AtomicLong(0);
public long increment() { return version.incrementAndGet();}
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); }
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);}
Atomic Updates
public static VarHandle findVersionVarHandle() { try { return MethodHandles.lookup(). findFieldVarHandle(Record.class, "version", long.class); } catch (Exception e) { throw new Error(e); }}
Memory Management
public long memory() { long address = UNSAFE.allocateMemory(8); UNSAFE.putLong(address, Long.MAX_VALUE); return UNSAFE.getLong(address);}
Memory Management
public long memory() { ByteBuffer byteBuffer = ByteBuffer.allocateDirect(8); byteBuffer.putLong(0, Long.MAX_VALUE); return byteBuffer.getLong(0);}
Memory Management
public long memory() { ByteBuffer byteBuffer = ByteBuffer.allocateDirect(8); VarHandle bufferView = MethodHandles.byteBufferViewVarHandle(long[].class, true); bufferView.set(byteBuffer, 0, Long.MAX_VALUE); return bufferView.get(byteBuffer, 0); }
Memory Management
public long memory() { MemoryRegion region = MemoryRegion.allocateNative ("myname", MemoryRegion.UNALIGNED, Long.MAX_VALUE); VarHandle regionView = MethodHandles. memoryRegionViewVarHandle(long[].class, true); regionView.set(region, 0, Long.MAX_VALUE); return regionView.get(region, 0);}
Memory Management
public long memory() { MemoryRegion region = MemoryRegion.allocateNative ("myname", MemoryRegion.UNALIGNED, Long.MAX_VALUE); VarHandle regionView = MethodHandles. memoryRegionViewVarHandle(long[].class, true); regionView.set(region, 0, Long.MAX_VALUE); return regionView.get(region, 0);}
Memory Management
public long memory(long index) { try (Scope scope = new NativeScope()) { Pointer<Long> ptr = scope.allocate( NativeLibrary.createLayout(long.class), numElements); Reference<Long> ref = ptr.offset(index).deref(); ref.set(Long.MAX_VALUE); return ref.get(); }}
Deserialization
private static final Unsafe UNSAFE = findUnsafe();private static final long VALUE_OFFSET = findValueOffset();public String deserializeString() throws Exception { char[] chars = readCharsFromStream(); String allocated = (String) UNSAFE.allocateInstance(String.class); UNSAFE.putObjectVolatile(allocated, VALUE_OFFSET, chars); return allocated;}
Deserialization
public String deserializeString() throws Exception { char[] chars = readCharsFromStream().freeze(); ReflectionFactory reflectionFactory = ReflectionFactory.getReflectionFactory(); Constructor<String> constructor = reflectionFactory .newConstructorForSerialization(String.class, char[].class); return constructor.newInstance(chars);}
Interoperabilityextern c { JNIEXPORT int JNICALL Java_ProcessIdentifier_getProcessId(JNIEnv *, jobject);} JNIEXPORT int JNICALLJava_ProcessIdentifier_getProcessId(JNIEnv *env, jobject thisObj) { return getpid();} public class ProcessIdentifier { static { System.loadLibrary("processidentifier"); } public native void talk();}
Interoperability
interface LibC { void getpid();} public int call() { LibC c = LibraryLoader.create(LibC.class).load("c"); return c.getpid();}
Interoperability
public void call() { MethodType intType = MethodType.methodType(int.class); MethodHandle handle = MethodHandles .findNative(null, “getpid”, intType); return (int) handle.invokeExact();}
Value Types
value class Point { final int x; final int y; } // Create a Point instancePoint point = makeValue(1, 2);
Value Types
int[] values = new int[2];int x = values[0]; int y = values[1];
Stack
var X
var Y
Stack Allocation!
Specialized Generics
class Box<any T> { void set(T element) { ... }; T get() { ... };}
Box<int> Box${T=int}.classBox<String> Box${T=java.lang.String}.classBox<RandomClass> Box${T=RandomClass}.class
Specialized Genericspublic void generics() { Box<int> intBox = new Box<>(); intBox.set(1); int intValue = intBox.get(); Box<String> stringBox = new Box<>(); stringBox.set("hello"); String stringValue = stringBox.get(); Box<RandomClass> box = new Box<>(); box.set(new RandomClass()); RandomClass value = box.get();}
Arrays 2.0int[] values = new int[Long.MAX_VALUE];
Arrays.chop(T[] a, int newlength);
Box<String>[] ... = new Box<String>[];
Arrays 2.0int[] values = new int[Long.MAX_VALUE];
Arrays.chop(T[] a, int newlength);
Box<int>[] ... = new Box<int>[];
Arrays 2.0int[] values = new int[Long.MAX_VALUE];
Arrays.chop(T[] a, int newlength);
Box<int>[] ... = new Box<int>[];
char[] frozen = new char[20L].freeze();
Arrays 2.0int[] 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]
Cleaner APIpackage java.nio;
import sun.misc.Cleaner;
class DirectByteBuffer extends MappedByteBuffer implements DirectBuffer {
private static class Deallocator implements Runnable { private Deallocator(long address, long size, int capacity) {...}
public void run() {...} }
private final Cleaner cleaner; DirectByteBuffer(int cap) { ... cleaner = Cleaner.create(this, new Deallocator(base, size, cap)); } }
Cleaner API
public selfCleaning() { try(AutoCleaning autoCleaning : new AutoCleaning()) { // do your thing here } }
Cleaner APIpublic 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(); }}
Class Dynamic
•Templating functionality•Aspect-like approach•Group common functionality•Enhanced Proxies
Class Dynamic
•Templating functionality•Aspect-like approach•Group common functionality•Enhanced Proxies
R methodName(ARGS) { synchronized (this) { underlying.methodName(ARGS); }}
Collections::sychronizedMap
Class Dynamic
•Templating functionality•Aspect-like approach•Group common functionality•Enhanced Proxies
Logging / Tracing
R methodName(ARGS) { logger.trace("Before methodname X"); underlying.methodName(ARGS); logger.trace("After methodname X");}
Class Dynamic
•Templating functionality•Aspect-like approach•Group common functionality•Enhanced Proxies
Your Use Case!
SpinWait Hints
Hints the JVM that a busy spin’s upcoming
JVM CAN intrinsify to use a WAIT opcode for the specific platform / CPU
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
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);}
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/
Questions?github.com/noctarius
@noctarius2k
More information:• http://openjdk.java.net/jeps/193 • http://openjdk.java.net/jeps/254 • http://openjdk.java.net/jeps/285 • http://openjdk.java.net/projects/valhalla/ • http://cr.openjdk.java.net/~jrose/arrays/frozen-array-value-rules.html • https://twitter.com/PaulSandoz/status/623891528432394240 • http://openjdk.java.net/projects/panama/ • http://cr.openjdk.java.net/~jrose/values/values-0.html • http://cr.openjdk.java.net/~briangoetz/valhalla/spec-classdyn.html • http://blog.codefx.org/java/dev/the-road-to-valhalla/
http://openjdk.java.net/jeps/259 http://openjdk.java.net/jeps/286 http://openjdk.java.net/jeps/276 http://openjdk.java.net/jeps/269 http://openjdk.java.net/jeps/266 http://openjdk.java.net/jeps/191 http://openjdk.java.net/jeps/186 http://openjdk.java.net/jeps/277