preface

Almost every tool, software infrastructure, high performance development library developed using Java, such as Netty, Cassandra, Hadoop, Kafka, etc., uses Sun.misc. Unsafe underneath.

The Unsafe class plays an important role in making Java more efficient and enhancing the underlying operations of the Java language. The Unsafe class, however, is under the Sun.misc package and is not part of the Java standard.

A long time ago, while reading the source code for the Unsafe class, I was wondering why it was named after the Unsafe class, given that it is used in parallel programming.

Broadening, however, the word Unsafe does not mean thread-safe, but rather that the Unsafe class is “dangerous” for the average programmer, and would not, and should not, be used by app developers.

Because the Unsafe class is so powerful, it provides lower-level functionality that can bypass the JVM. It gives Java the ability to manipulate memory space like C Pointers, which can improve efficiency, but also introduces pointer problems. It is not officially recommended, there is no documentation support, and it is even planned to be removed from older versions.

However, for developers, understanding the functionality provided by this class will help us learn more about CAS, concurrent programming and other related knowledge, and it is very necessary to learn and understand.

The structure of the Unsafe

The Unsafe class is “final” and does not allow inheritance, and the constructor is private, using a singleton pattern to retrieve the Unsafe class through a static method called getUnsafe().

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

Object creation in the singleton is limited in the getUnsafe method, which throws a SecurityException if it is a normal call. Only classes loaded by the main classloader can call this method.

So how do you get an object of the Unsafe class? Usually a reflection mechanism is used:

public static Unsafe getUnsafe() throws IllegalAccessException {
    Field unsafeField = Unsafe.class.getDeclaredFields()[0];
    unsafeField.setAccessible(true);
    return (Unsafe) unsafeField.get(null);
}
Copy the code

After acquiring an Unsafe object, you can “do whatever you want.” Here’s what you can do with the Unsafe method.

Unsafe’s key features

To get an overview of what Unsafe offers, consider the following:

Here are some important features to explain.

First, memory management

The Unsafe memory management functions include ordinary read/write, volatile read/write, ordered write, and direct memory operation to allocate and release memory.

General speaking, reading and writing

Unsafe allows an attribute of a class to be read and written, even if that attribute is private.

Public native int getInt(Object var1, long var2); Public native void putInt(Object var1, long var2, int var4); public native void putInt(Object var1, long var2, int var4);Copy the code

GetInt is used to read an int from the specified offset address of the object. PutInt is used to write an int at the specified offset address of the object. Other primitive types also provide corresponding methods.

In addition, the Unsafe getByte and putByte methods provide the ability to read and write directly to an address.

Volatile, speaking, reading and writing

Normal reads and writes do not guarantee visibility and order, whereas volatile reads and writes do.

Public native int getIntVolatile(Object var1, long var2); Public native void putIntVolatile(Object var1, long var2, int var4);Copy the code

Volatile reads and writes require visibility and order and are more expensive than normal reads and writes.

Order to write

Sequential writes only guarantee the order of writes, but not visibility, that is, writes from one thread are not immediately visible to other threads.

Public native void putOrderedInt(Object var1, long var2, int var4); public native void putOrderedInt(Object var1, long var2, int var4);Copy the code

PutOrderedXX writes are relatively inexpensive compared to volatile writes. PutOrderedXX writes are not guaranteed to be visible, but they are guaranteed to be ordered, meaning that instructions are not reordered.

Direct operating memory

Unsafe provides the ability to manipulate memory directly:

// allocateMemory public native long allocateMemory(long var1); Public native long reallocateMemory(long var1, long var3); Public native void setMemory(long var1, long var3, byte var5); Public native void copyMemory(Object var1, Long var2, Object var4, Long var5, long var7); Public native void freeMemory(long var1);Copy the code

Corresponding to the operation memory, also provides some methods to obtain memory information:

Public native long getAddress(long var1); public native int addressSize(); public native int pageSize();Copy the code

It is worth noting that the copyMemory method can be used to implement a generic object copy method. It is no longer necessary to implement clone method for every object, but only a shallow copy of the object can be achieved.

2. Non-routine object instantiation

Instead of instantiating objects via new or reflection, the Unsafe class provides the allocateInstance method, which generates object instances directly without calling constructors and other initialization methods.

This can be useful when an object is deserialized, allowing final fields to be rebuilt and set without having to call the constructor.

Public native Object allocateInstance(Class<? > var1) throws InstantiationException;Copy the code

Class loading

You can define and create a class by using the following methods.

// Method defines a Class for dynamically creating classes public native Class<? > defineClass(String var1, byte[] var2, int var3, int var4, ClassLoader var5, ProtectionDomain var6); Create an anonymous inner Class public native Class<? > defineAnonymousClass(Class<? > var1, byte[] var2, Object[] var3); Public native Boolean shouldBeInitialized(Class<? > var1); Public native void ensureClassInitialized(Class<? > var1);Copy the code

4. Offset correlation

Unsafe provides the following methods for retrieving Pointers to objects. By offsetting Pointers, you can not only directly modify the data to which the pointer points (even if they are private), but also find objects that the JVM has deemed garbage and can reclaim.

Public Native Long staticFieldOffset(Field var1) public native Long staticFieldOffset(Field var1) Public native long objectFieldOffset(Field var1) public native long objectFieldOffset(Field var1); Public Native Object staticFieldBase(Field var1); Public native int arrayBaseOffset(Class<? > var1); Public native int arrayIndexScale(Class<? > var1);Copy the code

Array manipulation

Array operations provide the following methods:

Public native int arrayBaseOffset(Class<? > var1); Public native int arrayIndexScale(Class<? > var1);Copy the code

ArrayBaseOffset is used in conjunction with arrayIndexScale to locate each element in the array in memory.

Because the maximum value of an array in Java is integer. MAX_VALUE, using the Unsafe class’s memory allocation method allows for very large arrays. In fact, such data can be considered a C array, so care needs to be taken to free memory at the right time.

Six, thread scheduling

Thread scheduling methods are as follows:

Public native void unpark(Object var1); Public native void park(Boolean var1, long var2); Public native void monitorEnter(Object var1); Public native void monitorExit(Object var1); Public native Boolean tryMonitorEnter(Object var1);Copy the code

Suspends a thread with the PARK method and it blocks until a timeout or interrupt condition occurs. The unpark method terminates a suspended thread and restores it to normal.

The suspension of threads in the entire concurrency framework is encapsulated in the LockSupport class, which has various versions of the pack methods, but ultimately calls the unsafe.park () method.

7. CAS operation

The CAS operation of the Unsafe class is probably the most used method. It provides a new solution to Java’s locking mechanism, through which classes such as AtomicInteger are implemented. The compareAndSwap method is atomic to avoid onerous locking mechanisms and improve code efficiency.

public final native boolean compareAndSwapObject(Object var1, long var2, Object var4, Object var5);
​
public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);
​
public final native boolean compareAndSwapLong(Object var1, long var2, long var4, long var6);
Copy the code

CAS is generally used for optimistic locking. It is widely used in Java, such as ConcurrentHashMap and ConcurrentLinkedQueue.

Memory barrier

JDK8 introduces a new method for defining memory barriers and avoiding code rearrangements:

Public native void loadFence(); public native void loadFence(); Public native void storeFence(); public native void storeFence(); Public native void fullFence(); public native void fullFence();Copy the code

Nine, other

Of course, there are a number of other methods available in the Insecure class, such as the CAS operation mentioned above. In the Example of AtomicInteger, when calling methods like getAndIncrement and getAndDecrement, Essentially, the Unsafe getAndAddInt method is called.

public final int getAndIncrement() {
    return unsafe.getAndAddInt(this, valueOffset, 1);
}
​
public final int getAndDecrement() {
    return unsafe.getAndAddInt(this, valueOffset, -1);
}
Copy the code

In practice, if you read other frameworks or library implementations, you can compare the overall functions of an Unsafe class with its application scenarios to get a rough idea of its functions.

summary

Addressing an Unsafe class, you probably know what it does when called the Unsafe class. The main purpose of using the Unsafe class is, in most cases, to improve performance and functionality. But at the same time, it also faces risks such as errors and memory management. Use is recommended only when necessary and in depth.

About the blogger: Author of the technology book SpringBoot Inside Technology, loves to delve into technology and writes technical articles.

Public account: “program new vision”, the blogger’s public account, welcome to follow ~

Technical exchange: Please contact the weibo user at Zhuan2quan