Unsafe in Java: Enabling Low-Level, C/C++-Style Operations

The Unsafe class, found in the sun.misc package, provides a powerful—yet dangerous—set of low-level operations. It bypasses standard Java access checks and memory safety mechanisms, making it a favorite tool for performance-critical libraries like Netty, Kafka, and Disruptor.

The name Unsafe is intentionally chosen to highlight the risks. While it opens the door to operations reminiscent of C/C++, misuse can lead to crashes, memory leaks, or unpredictable behavior. Use only when absolutely necessary and with a deep understanding.

How to Access Unsafe

This code uses Java reflection to access the private theUnsafe instance from the Unsafe class. Since direct access is restricted, setAccessible(true)` is used to bypass the access check.

Field f = Unsafe.class.getDeclaredField("theUnsafe");
f.setAccessible(true);
Unsafe unsafe = (Unsafe) f.get(null);

Note: In Java 17 and later, you may need to add the --add-opens JVM argument to allow access to internal packages like sun.misc.</br> Java 17+: use --add-opens java.base/sun.misc=ALL-UNNAMED if needed

Common Functionalities of Unsafe

1. Direct Memory Management

This code snippet demonstrates how to manually allocate, write to, read from, and free off-heap memory using the Unsafe class—similar to malloc and free in C.

long address = unsafe.allocateMemory(1024L); // Allocate 1024 bytes of off-heap memory
unsafe.putByte(address, (byte) 1);           // Write the byte value 1 to the allocated memory
byte value = unsafe.getByte(address);        // Read the byte value from the allocated memory
unsafe.freeMemory(address);                  // Free the previously allocated memory

Use MemorySegment from the Foreign Memory API (Java 19+)

2. Create Object Without Constructor

This snippet shows how to create an object instance without invoking its constructor using Unsafe.allocateInstance(). This technique is often used in serialization frameworks or advanced libraries where object instantiation needs to bypass constructor logic.

User user = (User) unsafe.allocateInstance(User.class); // Create an instance of User without calling its constructor
user.setName("John");                                   // Set the name property of the User object

Prefer ObjectInputStream.newInstance() in Java 17+

3. Raw Array Access

This code demonstrates how to use Unsafe to retrieve low-level memory layout information of an array—specifically, the base memory offset and the index scale (element size). These values can be used to perform manual memory access on array elements, similar to pointer in C/C++.

String[] names = {"Alice", "Bob", "Charlie"};           // Declare and initialize a String array
long base = unsafe.arrayBaseOffset(String[].class);     // Get the starting memory offset of the first element in the array
long scale = unsafe.arrayIndexScale(String[].class);    // Get the size (in bytes) of each array element step

No bounds checks — may crash JVM

4. CAS (Compare-And-Swap) Operations

This line performs an atomic Compare-And-Swap (CAS) operation using Unsafe. It checks whether the integer at a specific memory offset equals an expected value, and if so, updates it to a new value. CAS is a fundamental building block for implementing non-blocking concurrency in Java.

unsafe.compareAndSwapInt(this, stateOffset, expect, update); 
// Atomically sets the int value at the memory offset 'stateOffset' of the current object (this) 
// to 'update' if the current value equals 'expect'

Use VarHandle.compareAndSet() (Java 9+)

5. Thread Suspension and Resumption

This code demonstrates how to suspend and resume threads using Unsafe. The park method blocks the current thread, while unpark allows another thread to resume execution. These are low-level primitives used in concurrency utilities like LockSupport.

UNSAFE.park(false, 0L);// Suspends the current thread indefinitely until it's unparked or interrupted
UNSAFE.unpark(thread);// Wakes up (unparks) the specified thread if it's currently parked

6. Memory Fences (Barriers)

This code uses memory fences to prevent reordering of memory operations. loadFence(), storeFence(), and fullFence() enforce ordering guarantees for read, write, or all memory operations respectively. These are used in concurrent programming to maintain visibility and ordering without using locks.

unsafe.loadFence();   // Ensures all load (read) operations before this call are completed before any subsequent loads
unsafe.storeFence();  // Ensures all store (write) operations before this call are completed before any subsequent stores
unsafe.fullFence();   // Ensures all prior loads and stores are completed before any subsequent loads or stores

Use VarHandle.acquireFence() etc. (Java 9+)

Unsafe Capability Modern Alternative Since
Memory access MemorySegment (Foreign Memory API) Java 19+
CAS / atomic updates VarHandle.compareAndSet() Java 9+
Memory fences VarHandle.acquireFence() etc. Java 9+
Object instantiation ObjectInputStream.newInstance() Java 17+