Fundamentals 9 min read

Unlocking Java’s Unsafe: Access Low‑Level Memory and Concurrency Safely

This article explains the purpose and risks of Java's sun.misc.Unsafe class, categorizes its APIs, demonstrates how to obtain an Unsafe instance, and provides practical code examples for memory manipulation, CAS operations, thread control, object allocation, and building simple atomic utilities.

Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Unlocking Java’s Unsafe: Access Low‑Level Memory and Concurrency Safely

Unsafe Introduction

Unsafe, located in the

sun.misc

package, provides low‑level, potentially unsafe operations such as direct memory access and manual memory management, which can improve Java performance but also increase the risk of pointer‑related bugs.

Many classes in

java.util.concurrent.atomic

are implemented using Unsafe.

Unsafe APIs can be grouped into memory operations, CAS, class manipulation, object manipulation, thread control, system information, memory barriers, and array operations.

Memory‑Related Operations

CAS Operations

Atomic classes in

java.util.concurrent.atomic

rely on Unsafe.

<code>private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final long valueOffset;
static {
  try {
    valueOffset = unsafe.objectFieldOffset(AtomicInteger.class.getDeclaredField("value"));
  } catch (Exception ex) { throw new Error(ex); }
}
public final int getAndSet(int newValue) {
  return unsafe.getAndSetInt(this, valueOffset, newValue);
}</code>

Thread‑Related Operations

LockSupport uses

park

and

unpark

implemented via Unsafe.

<code>public static void park(Object blocker) {
  Thread t = Thread.currentThread();
  setBlocker(t, blocker);
  UNSAFE.park(false, 0L);
  setBlocker(t, null);
}
public static void unpark(Thread thread) {
  if (thread != null)
    UNSAFE.unpark(thread);
}</code>

Class‑Related Operations

Object Manipulation

System‑Related Operations

Memory Barriers

loadFence ensures all prior reads complete; storeFence ensures all prior writes complete; fullFence ensures both reads and writes complete.

Java 8’s

StampedLock

uses these barriers.

<code>private static final sun.misc.Unsafe U;
static {
  try {
    U = sun.misc.Unsafe.getUnsafe();
  } catch (Exception e) {
    throw new Error(e);
  }
}
public boolean validate(long stamp) {
  U.loadFence();
  return (stamp & SBITS) == (state & SBITS);
}</code>

U.loadFence();

Unsafe.java Overview

<code>public final class Unsafe {
    private static native void registerNatives();
    static {
        registerNatives();
        sun.reflect.Reflection.registerMethodsToFilter(Unsafe.class, "getUnsafe");
    }
    private Unsafe() {}
    private static final Unsafe theUnsafe = new Unsafe();
    // ...
}</code>

Obtaining an Unsafe Instance

Unsafe is a final singleton; the instance can be accessed via reflection:

Method 1

<code>Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
theUnsafe.setAccessible(true);
Unsafe unsafe = (Unsafe) theUnsafe.get(null);</code>

Method 2

<code>private static Unsafe unsafe = null;
static {
    try {
        Constructor<Unsafe> cons = Unsafe.class.getDeclaredConstructor();
        cons.setAccessible(true);
        unsafe = cons.newInstance();
    } catch (Exception e) {
        e.printStackTrace();
    }
}</code>

Simple Unsafe Usage Example

<code>int i = 0;
public static void main(String[] args) throws Exception {
    UnsafeDemo d = new UnsafeDemo();
    Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
    theUnsafe.setAccessible(true);
    Unsafe unsafe = (Unsafe) theUnsafe.get(null);
    Field f = UnsafeDemo.class.getDeclaredField("i");
    long fieldOffset = unsafe.objectFieldOffset(f);
    System.out.println(fieldOffset);
    boolean success = unsafe.compareAndSwapInt(d, fieldOffset, 0, 10);
    System.out.println(success);
    System.out.println(d.i);
}</code>

Unsafe Object Operations

<code>private static Unsafe unsafe = null;
static {
    try {
        Constructor<Unsafe> cons = Unsafe.class.getDeclaredConstructor();
        cons.setAccessible(true);
        unsafe = cons.newInstance();
    } catch (Exception e) {
        e.printStackTrace();
    }
}
public static void allocate() {
    try {
        Person p = (Person) unsafe.allocateInstance(Person.class);
        p.setId("s001");
        System.out.println(p.getValue());
        System.out.println(p.getId());
    } catch (Exception e) {
        e.printStackTrace();
    }
}</code>

Result:

Object Operation Example 2

<code>private Person p = new Person("1", "张三");
public static void main(String[] args) throws Exception {
    UnSafeObjectDemo d = new UnSafeObjectDemo();
    Field field = Unsafe.class.getDeclaredField("theUnsafe");
    field.setAccessible(true);
    Unsafe unsafe = (Unsafe) field.get(null);
    Field f = d.getClass().getDeclaredField("p");
    long offset = unsafe.objectFieldOffset(f);
    System.out.println(offset);
    boolean res = unsafe.compareAndSwapObject(d, offset, d.p, new Person("2", "李四"));
    System.out.println(res);
    System.out.println(d.p.getName());
}</code>

Creating Objects with Unsafe

When the constructor is unknown or should be bypassed, use reflection factories:

<code>Constructor<Teacher> cons = (Constructor<Teacher>) ReflectionFactory.getReflectionFactory()
    .newConstructorForSerialization(Teacher.class, Object.class.getConstructor());
cons.setAccessible(true);
Teacher t = cons.newInstance();
System.out.println(t);
</code>

Simple Atomic Class Using Unsafe

<code>public class AtomicCount {
    private static Unsafe unsafe;
    private int value;
    private static long valueOffset;
    static {
        try {
            Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
            theUnsafe.setAccessible(true);
            unsafe = (Unsafe) theUnsafe.get(null);
            Field f = AtomicCount.class.getDeclaredField("value");
            valueOffset = unsafe.objectFieldOffset(f);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    public AtomicCount(int value) { this.value = value; }
    public final int get() { return value; }
    public final int getAndIncrement() { return unsafe.getAndAddInt(this, valueOffset, 1); }
}</code>

End of article.

JavaConcurrencyreflectionUnsafeatomic operationslow-level memory
Spring Full-Stack Practical Cases
Written by

Spring Full-Stack Practical Cases

Full-stack Java development with Vue 2/3 front-end suite; hands-on examples and source code analysis for Spring, Spring Boot 2/3, and Spring Cloud.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.