1. Overview
In this article, we’ll have a look at a very interesting class provided by the JDK – Unsafe from the sun.misc package. This class provides us with low-level mechanisms that were designed to be used only by the core Java library and not by standard users.
This provides us with low-level mechanisms primarily designed for internal use within the core libraries.
2. Obtaining an Instance of the Unsafe
Firstly, to be able to use the Unsafe class, we need to get an instance – which is not straightforward given the class was designed only for the internal usage.
The way to obtain the instance is via the static method getUnsafe(). The caveat is that by default – this will throw a SecurityException.
Fortunately, we can obtain the instance using reflection:
Field f = Unsafe.class.getDeclaredField("theUnsafe"); f.setAccessible(true); unsafe = (Unsafe) f.get(null);
3. Instantiating a Class Using Unsafe
Let’s say that we have a simple class with a constructor that sets a variable value when the object is created:
class InitializationOrdering { private long a; public InitializationOrdering() { this.a = 1; } public long getA() { return this.a; } }
When we initialize that object using the constructor, the getA() method will return a value of 1:
InitializationOrdering o1 = new InitializationOrdering(); assertEquals(o1.getA(), 1);
But we can use the allocateInstance() method using Unsafe. It will only allocate the memory for our class, and will not invoke a constructor:
InitializationOrdering o3 = (InitializationOrdering) unsafe.allocateInstance(InitializationOrdering.class); assertEquals(o3.getA(), 0);
Notice the constructor was not invoked and due to that fact, the getA() method returned the default value for the long type – which is 0.
4. Altering Private Fields
Let’s say that we have a class that holds a secret private value:
class SecretHolder { private int SECRET_VALUE = 0; public boolean secretIsDisclosed() { return SECRET_VALUE == 1; } }
Using the putInt() method from Unsafe, we can change a value of the private SECRET_VALUE field, changing/corrupting the state of that instance:
SecretHolder secretHolder = new SecretHolder(); Field f = secretHolder.getClass().getDeclaredField("SECRET_VALUE"); unsafe.putInt(secretHolder, unsafe.objectFieldOffset(f), 1); assertTrue(secretHolder.secretIsDisclosed());
Once we get a field by the reflection call, we can alter its value to any other int value using the Unsafe.
5. Throwing an Exception
The code that is invoked via Unsafe is not examined in the same way by the compiler as regular Java code. We can use the throwException() method to throw any exception without restricting the caller to handle that exception, even if it’s a checked exception:
@Test(expected = IOException.class) public void givenUnsafeThrowException_whenThrowCheckedException_thenNotNeedToCatchIt() { unsafe.throwException(new IOException()); }
After throwing an IOException, which is checked, we don’t need to catch it nor specify it in the method declaration.
6. Off-Heap Memory
If an application is running out of available memory on the JVM, we could end up forcing the GC process to run too often. Ideally, we would want a special memory region, of the heap and not controlled by the GC process.
The allocateMemory() method from the Unsafe class gives us the ability to allocate huge objects off the heap, meaning that this memory will not be seen and taken into account by the GC and the JVM.
This can be very useful, but we need to remember that this memory needs to be managed manually and properly reclaiming with freeMemory() when no longer needed.
Let’s say that we want to create the large off-heap memory array of bytes. We can use the allocateMemory() method to achieve that:
class OffHeapArray { private final static int BYTE = 1; private long size; private long address; public OffHeapArray(long size) throws NoSuchFieldException, IllegalAccessException { this.size = size; address = getUnsafe().allocateMemory(size * BYTE); } private Unsafe getUnsafe() throws IllegalAccessException, NoSuchFieldException { Field f = Unsafe.class.getDeclaredField("theUnsafe"); f.setAccessible(true); return (Unsafe) f.get(null); } public void set(long i, byte value) throws NoSuchFieldException, IllegalAccessException { getUnsafe().putByte(address + i * BYTE, value); } public int get(long idx) throws NoSuchFieldException, IllegalAccessException { return getUnsafe().getByte(address + idx * BYTE); } public long size() { return size; } public void freeMemory() throws NoSuchFieldException, IllegalAccessException { getUnsafe().freeMemory(address); }
}
In the constructor of the OffHeapArray, we’re initializing the array that is of a given size. We are storing the beginning address of the array in the address field. The set() method is taking the index and the given value that will be stored in the array. The get() method is retrieving the byte value using its index that is an offset from the start address of the array.
Next, we can allocate that off-heap array using its constructor:
long SUPER_SIZE = (long) Integer.MAX_VALUE * 2; OffHeapArray array = new OffHeapArray(SUPER_SIZE);
We can put N numbers of byte values into this array and then retrieve those values, summing them up to test if our addressing works correctly:
int sum = 0; for (int i = 0; i < 100; i++) { array.set((long) Integer.MAX_VALUE + i, (byte) 3); sum += array.get((long) Integer.MAX_VALUE + i); } assertEquals(array.size(), SUPER_SIZE); assertEquals(sum, 300);
In the end, we need to release the memory back to the OS by calling freeMemory().
7. CompareAndSwap Operation
The very efficient constructs from the java.concurrent package, like AtomicInteger, are using the compareAndSwap() methods out of Unsafe underneath, to provide the best possible performance. This construct is widely used in the lock-free algorithms that can leverage the CAS processor instruction to provide great speedup compared to the standard pessimistic synchronization mechanism in Java.
We can construct the CAS based counter using the compareAndSwapLong() method from Unsafe:
class CASCounter { private Unsafe unsafe; private volatile long counter = 0; private long offset; private Unsafe getUnsafe() throws IllegalAccessException, NoSuchFieldException { Field f = Unsafe.class.getDeclaredField("theUnsafe"); f.setAccessible(true); return (Unsafe) f.get(null); } public CASCounter() throws Exception { unsafe = getUnsafe(); offset = unsafe.objectFieldOffset(CASCounter.class.getDeclaredField("counter")); } public void increment() { long before = counter; while (!unsafe.compareAndSwapLong(this, offset, before, before + 1)) { before = counter; } } public long getCounter() { return counter; } }
In the CASCounter constructor we are getting the address of the counter field, to be able to use it later in the increment() method. That field needs to be declared as the volatile, to be visible to all threads that are writing and reading this value. We are using the objectFieldOffset() method to get the memory address of the offset field.
The most important part of this class is the increment() method. We’re using the compareAndSwapLong() in the while loop to increment previously fetched value, checking if that previous value changed since we fetched it.
If it did, then we are retrying that operation until we succeed. There is no blocking here, which is why this is called a lock-free algorithm.
We can test our code by incrementing the shared counter from multiple threads:
int NUM_OF_THREADS = 1_000; int NUM_OF_INCREMENTS = 10_000; ExecutorService service = Executors.newFixedThreadPool(NUM_OF_THREADS); CASCounter casCounter = new CASCounter(); IntStream.rangeClosed(0, NUM_OF_THREADS - 1) .forEach(i -> service.submit(() -> IntStream .rangeClosed(0, NUM_OF_INCREMENTS - 1) .forEach(j -> casCounter.increment())));
Next, to assert that state of the counter is proper, we can get the counter value from it:
assertEquals(NUM_OF_INCREMENTS * NUM_OF_THREADS, casCounter.getCounter());
8. Park/Unpark
There are two very interesting methods in the Unsafe API that are used by the JVM to context switch threads. When the thread is waiting for some action, the JVM can make this thread blocked by using the park() method from the Unsafe class.
It is very similar to the Object.wait() method, but it is calling the native OS code, thus taking advantage of some architecture specifics to get the best performance.
When the thread is blocked and needs to be made runnable again, the JVM uses the unpark() method. We’ll often see those method invocations in thread dumps, especially in the applications which use thread pools.
9. Conclusion
In this article, we were looking at the Unsafe class and its most useful construct.
We saw how to access private fields, how to allocate off-heap memory, and how to use the compare-and-swap construct to implement lock-free algorithms.
The implementation of all these examples and code snippets can be found over on GitHub – this is a Maven project, so it should be easy to import and run as it is.