Java Unsafe APIs

Java Unsafe APIs provides various low-level APIs which could be used by programmers to do things related to:

  1. Off-Heap Memory Management
    • Off-Heap Memory Allocations
    • Reading and Putting of Integers, Long, Char, Short, Byte, Float, Double in Off-Heap Memory
  2. Manipulating Class Elements
    • Unsafe APIs provides various methods for retrieving or manipulating the different Class Elements – methods, variables.
  3. Manipulating Object/Instance Members
    • Unsafe APIs also provides various methods for retrieving and changing the state of the different variables/members in an object.

Why is it called unsafe?

  1. Using Traditional Java APIs provides us with strong guarantees that using them will not return SIGSEV and hence will not result in the killing of the JAVA process. This is mainly because of the fact that for most of the APIs we already have java internal checks which make sure that we are not accessing any memory location which we are not supposed to access and if we did anything which we are not supposed to do, then JAVA blankets our wrongdoing and then throws an exception to notify us. So essentially with these traditional JAVA API’s we never reach to a state where our processing leads to a core dump or SIGSEV.But with these unsafe APIs, there is no more such guarantee because these are mostly the low-level APIs some of them which are itself used by JAVA with some higher level checks. With these APIs, we can potentially access some unaccessible memory location due to which OS might complain and eventually might kill our application. So hence they are unsafe.To illustrate this with an example lets try to access a memory location unsafe.getAddress.
    public static void main(String args[]) {
        long address = unsafe.allocateMemory(1000);
        unsafe.getAddress(address + 100000000L);
    }

    In this example, we get this error and our JVM process is killed

    #
    # A fatal error has been detected by the Java Runtime Environment:
    #
    # SIGSEGV (0xb) at pc=0x000000010c781048, pid=87901, tid=0x0000000000001c03
    #
    # JRE version: Java(TM) SE Runtime Environment (8.0_131-b11) (build 1.8.0_131-b11)
    # Java VM: Java HotSpot(TM) 64-Bit Server VM (25.131-b11 mixed mode bsd-amd64 compressed oops)
    # Problematic frame:
    # V [libjvm.dylib+0x581048] Unsafe_GetNativeAddress+0x31
  2. Another very interesting usage of unsafe is to the access and/or even modify the private members of an instance which are otherwise not accessible via normal routes. This unsafe functionality feels like a backdoor entry to some code and so should be used only and only when required and hence the name unsafe 😀 😀
    public class A {
     private int p = 10;
    
     public A() {
     }
    }
    
    public static void main(String args[]) {
     Field f = A.class.getDeclaredField("p");
     A a = new A();
     f.setAccessible(true);
     f.set(a, 1);
     System.out.println("Value of a is " + f.get(a));
    }
    // Value of a is 1

    Note: Field.Set already uses unsafe methods for accessing/modifying the
    state of the members in a class.

Performance of Unsafe

One of the more important use cases of unsafe APIs is for accessing or/and modifying the off-heap memory. This helps in cases when we have a heavy cache and we want to keep that in memory to reduce the latencies but cannot keep in heap because of the GC issues/latencies. So our best bet is storing this off-heap and relying on unsafe APIs to access the cache.

Lets firstly see the performance numbers for in-heap and off-heap memory access.

public class MyBenchmark {

    public static Unsafe unsafe;
    public static long initialAddress;

    static {
        try {
            unsafe = getUnsafe();
            initialAddress = unsafe.allocateMemory(100000);
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }

    @SuppressWarnings("restriction")
    private static Unsafe getUnsafe() throws NoSuchFieldException, IllegalAccessException {
        try {

            Field singleoneInstanceField = Unsafe.class.getDeclaredField("theUnsafe");
            singleoneInstanceField.setAccessible(true);
            return (Unsafe) singleoneInstanceField.get(null);

        } catch (IllegalArgumentException e) {
            throw e;
        } catch (SecurityException e) {
            throw e;
        } catch (NoSuchFieldException e) {
            throw e;
        } catch (IllegalAccessException e) {
            throw e;
        }
    }

    @Benchmark
    public void onHeapStorage(BlackHole blackhole) {
        OnHeap onHeap = new OnHeap();
        onHeap.setField(100);
        blockhole.consume(onHeap.getField());
    }

    @Benchmark
    public void offHeapStorage(BlackHole blackhole) {
        OffHeap offHeap = new OffHeap(initialAddress);
        offHeap.setField(unsafe, 100);
        blockhole.consume(offHeap.getField(unsafe));
    }
}

See this link for more code details.

Benchmark Results

BenchmarkMode  Samples  Score     Score          error     Units
onHeapStorage   thrpt    200   1798771909.287 8732367.558  ops/s
offHeapStorage  thrpt    200    993357583.727 66354831.212 ops/s

OffHeapStorage using Unsafe APIs
Result: 1 Million operations/second   [Average]

OnHeapStorage
Result: 1.8 Million operations/second [Average]

We can clearly see that on heap storage outperforms off-heap storage using Unsafe APIs. This is mainly because of these facts :

  1. We are essentially copying the value 100 from inside the heap to off-heap. This essentially means some extra instructions. This is done when we call offHeap.setField(unsafe, 100) which is essentially copying 100 value to the off-heap memory location.tmp1
  2. We are also essentially copying the value 100 from off-heap to inside the heap which again means some extra instructions. This is done when we call offHeap.getField(unsafe) which essentially copies the value 100 from the off-heap memory location to inside heap.tmp2

As we can see that while using Unsafe APIs for off-heap storage there is some additional overhead, so we need to be really careful when to use these Unsafe APIs.

Use Cases of Unsafe APIs

  1. As already discussed earlier using unsafe APIs for off-heap storage comes with some performance penalty when compared to keeping the objects in heap. But there are certain times when we can benefit from this approach of using unsafe APIs for off-heap storage. This is mostly when we have a large cache to manage. Then we don’t have the liberty of keeping the cache in heap because of long GC pauses which might affect the application latencies.
  2. Other use-cases of unsafe APIs include that these unsafe APIs also provide a way by which we can access any fields of an object even private given we know the offsets of those fields.
    public class NormalObject {
      public long a1 = 10;  // Offset = 16L
      private long a2 = 20; // Offset = 24L
      public long a3 = 30;  // Offset = 32L
    }
    
    NormalObject normalObject = new NormalObject();
    unsafe.getLong(normalObject, 16L); // returns 10
    unsafe.getLong(normalObject, 24L); // returns 20
    unsafe.getLong(normalObject, 32L); // returns 30

For Better Understanding also read

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

w

Connecting to %s