The problem

(1) What is Unsafe?

(2) Does Unsafe only have CAS functions?

(3) What is Unsafe about food?

(4) How to use Unsafe?

Introduction to the

Unaddressed is the first chapter on Java and unpacking, but instead of unpacking Java classes, its magic class sun.misc.unsafe.

Unsafe gives us access to low-level mechanisms that are only for use by the Java core library and should not be used by ordinary users.

However, in order to better understand the Java ecosystem, we need to learn about it, to understand it, not to dive into the underlying C/C++ code, but to understand its basic functionality.

Gets an instance of Unsafe

Looking at Unsafe’s source code, it provides a static method for getUnsafe().

@CallerSensitive
public static Unsafe getUnsafe(a) {
    Class var0 = Reflection.getCallerClass();
    if(! VM.isSystemDomainLoader(var0.getClassLoader())) {throw new SecurityException("Unsafe");
    } else {
        returntheUnsafe; }}Copy the code

However, calling this method directly throws a SecurityException, because Unsafe is only for Use by Java inner classes and should not be used by external classes.

So, we don’t have a way?

Of course not! We have reflexes! Looking at the source code, we found that it has a property called theUnsafe, which we just need to get from reflection.

public class UnsafeTest {
    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
        Field f = Unsafe.class.getDeclaredField("theUnsafe");
        f.setAccessible(true);
        Unsafe unsafe = (Unsafe) f.get(null); }}Copy the code

Instantiate a class using Unsafe

Suppose we have a simple class like this:

class User {
    int age;

    public User(a) {
        this.age = 10; }}Copy the code

If we instantiate the class using the constructor, the age property will return 10.

User user1 = new User();
/ / print 10
System.out.println(user1.age);
Copy the code

What if we call Unsafe to instantiate?

User user2 = (User) unsafe.allocateInstance(User.class);
/ / print 0
System.out.println(user2.age);
Copy the code

Age will return 0, because unsafe.allocateInstance () only allocates memory to the object and does not call the constructor, so only the default value 0 of int will be returned.

Modify the value of a private field

Using the Unsafe putXXX() method, we can modify the value of any private field.

public class UnsafeTest {
    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, InstantiationException {
        Field f = Unsafe.class.getDeclaredField("theUnsafe");
        f.setAccessible(true);
        Unsafe unsafe = (Unsafe) f.get(null);

        User user = new User();
        Field age = user.getClass().getDeclaredField("age");
        unsafe.putInt(user, unsafe.objectFieldOffset(age), 20);

        / / print 20System.out.println(user.getAge()); }}class User {
    private int age;

    public User(a) {
        this.age = 10;
    }

    public int getAge(a) {
        returnage; }}Copy the code

Once we get the field age through the reflection call, we can change its value to any other int value using Unsafe. (Of course, this can also be modified directly by reflection)

Throwing checked Exceptions

We know that if our code throws a Checked exception, we can use try… Catch catches it or defines the exception on the method signature, whereas with Unsafe we can throw a checked exception without having to catch or define it on the method signature.

// To throw IOException in the normal way, you need to define throwing out of the method signature
public static void readFile(a) throws IOException {
    throw new IOException();
}
// Using Unsafe to throw exceptions does not require the definition to be thrown outward on the method signature
public static void readFileUnsafe(a) {
    unsafe.throwException(new IOException());
}
Copy the code

Use out-of-heap memory

If the process runs out of memory on the JVM, this can lead to frequent GC. Ideally, we can consider using off-heap memory, which is a piece of memory that is not managed by the JVM.

With Unsafe’s allocateMemory(), we can allocateMemory directly out of the heap, which can be very useful, but remember that this memory is not managed by the JVM, so we’ll call the freeMemory() method to manually free it.

Suppose we want to create a huge array of ints outside the heap, we can do this using the allocateMemory() method:

class OffHeapArray {
    // An int equals 4 bytes
    private static final int INT = 4;
    private long size;
    private long address;

    private static Unsafe unsafe;
    static {
        try {
            Field f = Unsafe.class.getDeclaredField("theUnsafe");
            f.setAccessible(true);
            unsafe = (Unsafe) f.get(null);
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch(IllegalAccessException e) { e.printStackTrace(); }}// the constructor allocates memory
    public OffHeapArray(long size) {
        this.size = size;
        // Number of bytes
        address = unsafe.allocateMemory(size * INT);
    }
    
    // Gets the element at the specified index
    public int get(long i) {
        return unsafe.getInt(address + i * INT);
    }
    // Sets the element at the specified index
    public void set(long i, int value) {
        unsafe.putInt(address + i * INT, value);
    }
    // Number of elements
    public long size(a) {
        return size;
    }
    // Free the off-heap memory
    public void freeMemory(a) { unsafe.freeMemory(address); }}Copy the code

Call allocateMemory() in the constructor to allocateMemory, and call freeMemory() to freeMemory when use is complete.

The usage is as follows:

OffHeapArray offHeapArray = new OffHeapArray(4);
offHeapArray.set(0.1);
offHeapArray.set(1.2);
offHeapArray.set(2.3);
offHeapArray.set(3.4);
offHeapArray.set(2.5); // Repeat the element at index 2

int sum = 0;
for (int i = 0; i < offHeapArray.size(); i++) {
    sum += offHeapArray.get(i);
}
/ / print 12
System.out.println(sum);

offHeapArray.freeMemory();
Copy the code

Finally, be sure to call freeMemory() to freeMemory back to the operating system.

CompareAndSwap operation

CAS operations are heavily used under JUC, underpinned by calls to the Unsafe CompareAndSwapXXX() method. This approach is widely used in lock-free algorithms and can take advantage of CAS processor instructions to provide significant acceleration over the standard pessimistic locking mechanism in Java.

For example, we can build thread-safe counters based on Unsafe’s compareAndSwapInt() method.

class Counter {
    private volatile int count = 0;

    private static long offset;
    private static Unsafe unsafe;
    static {
        try {
            Field f = Unsafe.class.getDeclaredField("theUnsafe");
            f.setAccessible(true);
            unsafe = (Unsafe) f.get(null);
            offset = unsafe.objectFieldOffset(Counter.class.getDeclaredField("count"));
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch(IllegalAccessException e) { e.printStackTrace(); }}public void increment(a) {
        int before = count;
        // If it fails, try again until it succeeds
        while(! unsafe.compareAndSwapInt(this, offset, before, before + 1)) { before = count; }}public int getCount(a) {
        returncount; }}Copy the code

We define a volatile field count so that changes to it are visible to all threads and obtain the offset address of count in the class when the class is loaded.

In the increment() method, we attempt to update the previously acquired count value by calling Unsafe’s compareAndSwapInt() method. If it has not been updated by another thread, the update succeeds; otherwise, we retry until it succeeds.

We can test our code by using multiple threads:

Counter counter = new Counter();
ExecutorService threadPool = Executors.newFixedThreadPool(100);

// Add 10000 times for each thread
IntStream.range(0.100)
    .forEach(i->threadPool.submit(()->IntStream.range(0.10000)
        .forEach(j->counter.increment())));

threadPool.shutdown();

Thread.sleep(2000);

/ / print 1000000
System.out.println(counter.getCount());
Copy the code

park/unpark

The JVM uses the unsafe-looking methods Park () and unpark() for context switches.

The JVM calls Unsafe’s park() method to block a thread that is waiting for an operation.

The JVM calls the Unsafe unpark() method to wake up a blocked thread when it needs to run again.

We saw a lot of locksupport.park ()/unpark() when analyzing collections in Java earlier, both of which are underlying calls to the Unsafe methods.

conclusion

Using Unsafe, you can do almost anything:

1) Instantiate a class;

(2) Modify the value of the private field;

(3) Throw a checked exception;

(4) Use out-of-heap memory;

(5) CAS operation;

(6) block/wake up the thread;

eggs

How to instantiate a class?

(1) Instantiate a class using a constructor;

(2) Instantiate a Class by Class;

(3) Instantiate a class by reflection;

(4) instantiate a class by cloning;

(5) Instantiate a class by deserialization;

(6) Instantiate a class via Unsafe;

public class InstantialTest {

    private static Unsafe unsafe;
    static {
        try {
            Field f = Unsafe.class.getDeclaredField("theUnsafe");
            f.setAccessible(true);
            unsafe = (Unsafe) f.get(null);
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch(IllegalAccessException e) { e.printStackTrace(); }}public static void main(String[] args) throws Exception {
        // 1
        User user1 = new User();
        // 2. Class, which is actually reflection
        User user2 = User.class.newInstance();
        / / 3. Reflection
        User user3 = User.class.getConstructor().newInstance();
        / / 4. Cloning
        User user4 = (User) user1.clone();
        // 5. Deserialize
        User user5 = unserialize(user1);
        // 6. Unsafe
        User user6 = (User) unsafe.allocateInstance(User.class);

        System.out.println(user1.age);
        System.out.println(user2.age);
        System.out.println(user3.age);
        System.out.println(user4.age);
        System.out.println(user5.age);
        System.out.println(user6.age);
    }

    private static User unserialize(User user1) throws Exception {
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("D://object.txt"));
        oos.writeObject(user1);
        oos.close();

        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("D://object.txt"));
        // deserialize
        User user5 = (User) ois.readObject();
        ois.close();
        return user5;
    }

    static class User implements Cloneable.Serializable {
        private int age;

        public User(a) {
            this.age = 10;
        }

        @Override
        protected Object clone(a) throws CloneNotSupportedException {
            return super.clone(); }}}Copy the code

Welcome to pay attention to my public number “Tong Elder brother read source code”, view more source code series articles, with Tong elder brother tour the ocean of source code.