do we need unsafe in java?
TRANSCRIPT
Do we need Unsafe in Java?
Andrei [email protected]
Mystic sun.misc.Unsafe
3
Can I override object with sun.misc.Unsafe?
Is there a way to force unload a classby using the sun.misc.Unsafe class?
Can one break a security manager with sun.misc.Unsafe?
Rescuing a swallowed Exception in Java
How to free memory using Java Unsafe, using a Java reference?
Create a mutable java.lang.String
Mystic sun.misc.Unsafe
4 Mystic sun.misc.Unsafe
How to free memory using Java Unsafe, using a Java reference?
Player p = (Player) unsafe.allocateInstance(Player.class);
// Is there a way of accessing the memory address// from the object reference
unsafe.freeMemory(p.hashCode());
http://stackoverflow.com/questions/24429777
5 Mystic sun.misc.Unsafe
Why it is called Unsafe
## A fatal error has been detected by the Java Runtime Environment:## SIGSEGV (0xb) at pc=0x00002ad221034ee5, pid=3680, tid=1091057984## JRE version: Java(TM) SE Runtime Environment (8.0_60-b27) (build 1.8.0_60-b27)# Java VM: Java HotSpot(TM) 64-Bit Server VM (25.60-b23 mixed mode linux-amd64)# Problematic frame:# C [libc.so.6+0x6dee5] cfree+0x25## An error report file with more information is saved as:# /home/apangin/hs_err_pid3680.log#
6
What is Unsafe actually
• Bridge between Class Library and JVM• For use inside JDK
- NIO- AWT- Reflection- java.util.concurrent- MethodHandles
• Exists in many JVMs, even on Android
Mystic sun.misc.Unsafe
7 Mystic sun.misc.Unsafe
public final class Unsafe {
static { registerNatives(); sun.reflect.Reflection.registerMethodsToFilter(Unsafe.class, "getUnsafe"); }
private Unsafe() {}
private static final Unsafe theUnsafe = new Unsafe();
@CallerSensitive public static Unsafe getUnsafe() { Class cc = Reflection.getCallerClass(); if (cc.getClassLoader() != null) throw new SecurityException("Unsafe"); return theUnsafe; }
8 Mystic sun.misc.Unsafe
Getting Unsafe
public static Unsafe getUnsafe() throws Exception { Field f = Unsafe.class.getDeclaredField("theUnsafe"); f.setAccessible(true); return (Unsafe) f.get(null);}
9
Security hole?
Security Manager is off by default
$ java -Djava.security.manager
java.security.AccessControlException: access denied ("java.lang.RuntimePermission" "accessClassInPackage.sun.misc")
Mystic sun.misc.Unsafe
10 Mystic sun.misc.Unsafe
You’ve been warned!
$ javac Test.java
Test.java:2: warning: Unsafe is internal proprietary API and may be removed in a future releaseimport sun.misc.Unsafe; ^
$ javac -XDignore.symbol.file Test.java
Inside Unsafe
12 Inside Unsafe
public native long allocateMemory(long bytes);public native long reallocateMemory(long address, long bytes);public native void freeMemory(long address);
public native byte getByte(long address);public native void putByte(long address, byte x);
public native int getInt(Object o, long offset);public native void putInt(Object o, long offset, int x);
public native long objectFieldOffset(Field f);public native long staticFieldOffset(Field f);public native int arrayBaseOffset(Class<?> arrayClass);
public native void copyMemory(Object srcBase, long srcOffset, Object destBase, long destOffset, long bytes);
13
package snow.misc;
public class MyUnsafe {
public native int getInt(long address);
#include <jni.h>
JNIEXPORT jint JNICALLJava_snow_misc_MyUnsafe(JNIEnv* env, jobject myUnsafe, jlong address) { return *(jint*)address;}
Inside Unsafe
14 Inside Unsafe
JNI call
1. Create stack frame2. Move arguments according to ABI3. Wrap objects into JNI handles4. Obtain JNIEnv* and jclass5. Trace method_entry6. Lock if synchronized7. Lazy lookup and linking8. in_java in_native thread transition9. Call the native function10. Check for safepoint11. Switch state to in_java12. Unlock if synchronized13. Notify method_exit14. Unwrap result, reset JNI handles block15. Handle exceptions16. Remove stack frame
15
Implementation
• Most methods are JVM intrinsics- E.g. getInt is a single MOV instruction
• Executed in Java or VM context- Beware of mapped I/O
(safepoint pauses)
Inside Unsafe
16
public native int getIntVolatile(Object o, long offset);public native void putIntVolatile(Object o, long offset, int x);public native void putOrderedInt(Object o, long offset, int x);
public final native boolean compareAndSwapInt(Object o, long offset, int expected, int x);
/* @since 1.8 */public native void loadFence();public native void storeFence();public native void fullFence();
public final int getAndAddInt(Object o, long offset, int delta) { int v; do { v = getIntVolatile(o, offset); } while (!compareAndSwapInt(o, offset, v, v + delta)); return v;}
Inside Unsafe
17
i.getAndAdd(5)
lock addl $0x5,0xc(%r10)loop:mov 0xc(%r10),%eaxmov %eax,%r11dadd $0x5,%r11dlock cmpxchg
%r11d,0xc(%r10)sete %r11bmovzbl %r11b,%r11dtest %r11d,%r11dje loop
JDK 7u72 -XX:+PrintAssembly JDK 8u60
1 2 3 40
20406080
100120140
83
46
15 11
132
105
45 43
threads
ops
/ μs
Inside Unsafe
18 Inside Unsafe
public native Class<?> defineClass(String name, byte[] b, int off, int len, ClassLoader loader, ProtectionDomain protectionDomain);
public native Class<?> defineAnonymousClass(Class<?> hostClass, byte[] data, Object[] cpPatches);
public native Object allocateInstance(Class<?> cls) throws InstantiationException;
public native void park(boolean isAbsolute, long time);public native void unpark(Object thread);
public native int getLoadAverage(double[] loadavg, int nelems);
public native void throwException(Throwable ee);
19 Inside Unsafe
Deprecated in JDK 8, removed in 9
@Deprecatedpublic native void monitorEnter(Object o);
@Deprecatedpublic native void monitorExit(Object o);
@Deprecatedpublic native boolean tryMonitorEnter(Object o);
// Phantom HotSpot intrinsicspublic native void prefetchRead(Object o, long offset);public native void prefetchWrite(Object o, long offset);
Use cases
21
Off-heap collections
• Lists, sets, maps- Large capacity > 2 GB- Predictable GC pauses- No heap fragmentation (CMS Promotion Failures)- Data locality
Use cases
22
Data locality
Key
ValueKey
Value
Map.Entry Off-heap layout
a b c a b c a b c
Use cases
23 Use cases
Off-heap hash map
0 addr 0 … addr 0
hash time nextkey (?)
value
hash time 0key (?)
value
entry
one.nio.mem.SharedMemoryMap
24 OffheapLongList
public OffheapLongList(int initialCapacity) { capacity = Math.max(initialCapacity, 4); base = unsafe.allocateMemory(capacity * 8L); }
public void add(long value) { if (size >= capacity) { capacity *= 2; base = unsafe.reallocateMemory(base, capacity * 8L); } unsafe.putLong(base + size * 8L, value); size++; }
public long get(int index) { if (index < 0 || index >= size) { throw new IndexOutOfBoundsException(); } return unsafe.getLong(base + index * 8L); }
25 Use cases
When to free memory?
class OffheapCleaner<T> extends PhantomReference<T> { static final ReferenceQueue<T> queue = new ReferenceQueue<T>(); final long address;
OffheapCleaner(T referent, long address) { super(referent, queue); this.address = address; }}
while (true) { OffheapCleaner<T> cleaner = (OffheapCleaner<T>) queue.remove(); unsafe.freeMemory(cleaner.address);}
26
Why not ByteBuffers?
• Size limit (Integer.MAX_VALUE)• Not thread-safe• No freeMemory
public void forceClean(ByteBuffer buffer) { ((sun.nio.ch.DirectBuffer) buffer)).cleaner().clean();}
Use cases
27 sun.nio.ch.FileChannelImpl
try { // If no exception was thrown from map0, the address is valid addr = map0(imode, mapPosition, mapSize); } catch (OutOfMemoryError x) { // An OutOfMemoryError may indicate that we've exhausted memory // so force gc and re-attempt map System.gc(); try { Thread.sleep(100); } catch (InterruptedException y) { Thread.currentThread().interrupt(); } try { addr = map0(imode, mapPosition, mapSize); } catch (OutOfMemoryError y) { // After a second OOME, fail throw new IOException("Map failed", y); } }
28 Cassandra
// Hoping GC will remove some not used tables.System.gc();
Thread.sleep(60000);
location = estimateFlushPath();
if (location!=null) return new File(location, ssTableFileName).getAbsolutePath();
// Hoping GC will remove some not used tables - sometimes 2 GC cycles are needed.logger_.warn("Still Insufficient disk space to flush "+ssTableFileName);System.gc();
Thread.sleep(60000);
location = estimateFlushPath();
29 Use cases
Direct buffers ↔ Unsafe
Field addressField = Buffer.class.getDeclaredField("address");addressField.setAccessible(true);
// ByteBuffer addresslong address = addressField.getLong(byteBuffer);
// address ByteBufferByteBuffer result = prototype.duplicate();addressField.setLong(result, address);
30 Use cases
Benchmark
Benchmark Mode Cnt Score Error Unitsb.byteBuffer thrpt 15 290,832 ± 7,858 ops/usb.rawMemory thrpt 15 480,799 ± 2,610 ops/us
public void byteBuffer() { buf.putLong(0, order.timestamp); buf.put(8, order.operationType); buf.putInt(9, order.productId); buf.putFloat(13, order.price);}
public void rawMemory() { unsafe.putLong(addr + 0, order.timestamp); unsafe.put(addr + 8, order.operationType); unsafe.putInt(addr + 9, order.productId); unsafe.putFloat(addr + 13, order.price);}
31
I/O and persistence
• Persistent caches and storages- Memory-mapped files (64-bit)- Shared memory
• Cooperation with OS- Pointer arithmetic, alignment- mlock, madvise etc.
Use cases
32 Use cases
Mapping large files
// MappingMethod map0 = FileChannelImpl.class.getDeclaredMethod( "map0", int.class, long.class, long.class);map0.setAccessible(true);long addr = (Long) map0.invoke(f.getChannel(), 1, 0L, f.length());
// UnmappingMethod unmap0 = FileChannelImpl.class.getDeclaredMethod( "unmap0", long.class, long.class);unmap0.setAccessible(true);unmap0.invoke(null, addr, length);
33
IPC, Messaging
• Concurrent off-heap buffers and queues• High-performance messaging
- Disruptor- Aeron: 6M msg/s
• Shared data structures- Chronicle Map
Use cases
34 Use cases
Coding / Decodingdo { v1 += getInt(buf, offset) * P2; v1 = ((v1 << 13) | (v1 >>> 19)) * P1;
v2 += getInt(buf, offset + 4) * P2; v2 = ((v2 << 13) | (v2 >>> 19)) * P1;
v3 += getInt(buf, offset + 8) * P2; v3 = ((v3 << 13) | (v3 >>> 19)) * P1;
v4 += getInt(buf, offset + 12) * P2; v4 = ((v4 << 13) | (v4 >>> 19)) * P1;} while ((offset += 16) <= limit);
public int getInt(byte[] buf, int off) { return (buf[off] & 0xff) | (buf[off + 1] & 0xff) << 8 | (buf[off + 2] & 0xff) << 16 | (buf[off + 3]) << 24;}public int getInt(byte[] buf, int off) { return unsafe.getInt(buf, byteArrayOffset + off);}
xxHash loop
35 Use cases
BenchmarkBenchmark (length) Mode Cnt Score Error Unitsb.xxhashArray 10 thrpt 5 61697,875 ± 849,565 ops/msb.xxhashArray 100 thrpt 5 13552,417 ± 91,039 ops/msb.xxhashArray 1000 thrpt 5 1749,843 ± 12,290 ops/msb.xxhashUnsafe 10 thrpt 5 77358,056 ± 675,536 ops/msb.xxhashUnsafe 100 thrpt 5 34153,796 ± 319,798 ops/msb.xxhashUnsafe 1000 thrpt 5 5259,813 ± 43,870 ops/ms
36
Serialization
• Fast conversion to/from byte[]• Access private fields
- Reflection vs. Unsafe• Instantiate objects without constructor
- unsafe.allocateInstance()
Use cases
37
one-nio serialization
• Extend sun.reflect.MagicAccessorImpl- no bytecode verification
• Fast and compact• Supports class evolution
- Java serialization- JBoss- Kryo- Protobuf
Use cases
38
Native APIs
• Own network library- Linux-specific TCP features and socket options- Integration with OpenSSL
• I/O and memory management- mlock- posix_fadvise
• setuid• sched_setaffinity
Use cases
Removal of sun.misc.Unsafe
40 Removal of sun.misc.Unsafe
An absolute disaster
Cassandra
Netty Guava
Hazelcast
Mockito
GWT
HadoopZookeeper
Kafka
Gson
Neo4j
Grails
Spring
Disruptor
JRuby
Scala
Akka
41
Why?
Removal of sun.misc.Unsafe
Integrity and security
Maintenance costs
42
Chronology
• JEP 200: The Modular JDK• Mark Reinhold speaks at Devoxx• Chris Engelbert started a discussion group• Apocalyptic post at blog.dripstat.com• JCrete • JVM Language Summit• JEP 260: Encapsulate Most Internal APIs
Removal of sun.misc.Unsafe
2014 / 07
2015 / 06
2015 / 07
2015 / 08
43
Migration plan
• Replacement in JDK 8 hide in JDK 9- sun.misc.BASE64Encoder etc.- Available via command-line flag
• No replacement in JDK 8 available outside- sun.misc.Unsafe, sun.reflect.ReflectionFactory- sun.misc.Cleaner, sun.misc.SignalHandler
• Replacement in JDK 9 hide in JDK 9,
remove in JDK 10
Removal of sun.misc.Unsafe
Replacements
45
Project Panama
• JEP 191: Foreign Function Interface• Expected in Java 10• JNR
Replacements
public interface LibC { int setuid(@uid_t long uid);}
LibC libc = LibraryLoader.create(LibC.class).load("c");libc.setuid(restrictedUser);
46
JEP 193: VarHandles
• Targeted for Java 9
Replacements
class Queue { int size; ...}
VarHandle queueSize = MethodHandles.lookup(). findVarHandle(Queue.class, "size", int.class);
queueSize.addAndGet(10);
47 Replacements
// Desired syntaxVarHandle queueSize = Queue::size;
VarHandle intElementHandle = MethodHandles.lookup() .arrayElementVarHandle(int[].class);
VarHandle longArrayViewHandle = MethodHandles.lookup() .arrayElementViewHandle(byte[].class, long[].class, true, false);
ByteBuffer byteBuffer = ByteBuffer.allocateDirect(8);VarHandle bufferView = MethodHandles.lookup() .byteBufferViewVarHandle(long[].class, true, false);
48 Replacements
public static void fullFence();public static void acquireFence()public static void releaseFence();public static void loadLoadFence();public static void storeStoreFence();
• getRelaxed, getVolatile, getOpaque• setRelaxed, setVolatile, setOpaque• getAquire, setRelease
• compareAndSet, compareAndExchangeVolatile• compareAndExchangeAcquire, compareAndExchangeRelease• weakCompareAndSet, weakCompareAndSetAcquire,
weakCompareAndSetRelease• getAndSet, getAndAdd, addAndGet
49 Replacements
// Not in Java 9MemoryRegion region = MemoryRegion.allocateNative( "myname", MemoryRegion.UNALIGNED, Long.MAX_VALUE);
VarHandle regionView = region.asVarHandle(long[].class, true, false);
regionView.set(region, 0, Long.MAX_VALUE);return regionView.get(region, 0);
50 Replacements
Serialization
ReflectionFactory reflectionFactory = ReflectionFactory.getReflectionFactory();
Constructor<?> baseConstructor = Object.class.getDeclaredConstructor();
Constructor<?> constructor = reflectionFactory. newConstructorForSerialization(Foo.class, baseConstructor);
return (Foo) constructor.newInstance();
jobject AllocObject(JNIEnv *env, jclass clazz); JNI
51 Replacements
Можно жить и без Unsafeно грустно
Unsafe bugs
53 Unsafe bugs
JVM Bugs
Вставка рисунка
## A fatal error has been detected by the Java Runtime Environment:## SIGSEGV (0xb) at pc=0x00002b9fd9f44898, pid=19692, tid=1096575296## JRE version: 6.0_24-b07# Java VM: Java HotSpot(TM) 64-Bit Server VM (19.1-b02 mixed mode linux-amd64)# Problematic frame:# J Test.loop(Lsun/misc/Unsafe;J)V## An error report file with more information is saved as:# /home/apangin/hs_err_pid19692.log## If you would like to submit a bug report, please visit:# http://java.sun.com/webapps/bugreport/crash.jsp#
for (int i = 0; i < BUF_SIZE; i += 8) unsafe.putLong(addr + i, Long.reverseBytes(i));
54
JVM Bugs
• JDK-6968348: Unsafe + Long.reverseBytes• JDK-7196566: Reported by me • JDK-8022783: Non-public bug in C2• JDK-8087134: Wrong C1 optimization
Unsafe bugs
6u25
7u40
7u71
8u60
-XX:-TieredCompilation
55 Unsafe bugs
Not yet fixed
RandomAccessFile raf = new RandomAccessFile("/dev/shm/cache.tmp", "rw");raf.setLength(5_000_000_000L);
MappedByteBuffer buf = raf.getChannel().map(READ_WRITE, 0, raf.length());for (int i = 0; i < 5000; i++) { buf.put(new byte[1_000_000]);} SIGBUS
Thank you
@AndreiPangin
57
Development @ OK
• Blog- http://habrahabr.ru/company/odnoklassniki
• Open source- https://github.com/odnoklassniki
• Career- http://v.ok.ru
Thank you