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-opensJVM argument to allow access to internal packages likesun.misc.</br> Java 17+: use--add-opens java.base/sun.misc=ALL-UNNAMEDif 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
MemorySegmentfrom 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+)
Future Trends and Recommended Alternatives
| 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+ |