Java Basic Unsafe Memory Operation Unsafe Class Detailed Explanation

  • 2021-09-11 20:19:15
  • OfStack

Brief introduction

The Unsafe class gives Java the ability to manipulate memory space like pointer 1 in C language, and directly manipulating memory means

1. It is not managed by jvm, which means that it cannot be managed by GC. We need to manually GC, and memory leakage will occur if we are careless.

2. In many methods of Unsafe, the original address (memory address) and the address of the replaced object must be provided, and the offset should be calculated by itself. Once a problem occurs, it is an exception of JVM crash level, which will cause the whole JVM instance to crash, showing that the application program directly drops crash.

3. Direct operation of memory also means that it is faster and can improve efficiency under the condition of high concurrency.

Class Unsafe


public final class Unsafe

The Unsafe class is "final" and does not allow inheritance.

Unsafe Properties


private static final Unsafe theUnsafe;
public static final int INVALID_FIELD_OFFSET = -1;
public static final int ARRAY_BOOLEAN_BASE_OFFSET;
public static final int ARRAY_BYTE_BASE_OFFSET;
public static final int ARRAY_SHORT_BASE_OFFSET;
public static final int ARRAY_CHAR_BASE_OFFSET;
public static final int ARRAY_INT_BASE_OFFSET;
public static final int ARRAY_LONG_BASE_OFFSET;
public static final int ARRAY_FLOAT_BASE_OFFSET;
public static final int ARRAY_DOUBLE_BASE_OFFSET;
public static final int ARRAY_OBJECT_BASE_OFFSET;
public static final int ARRAY_BOOLEAN_INDEX_SCALE;
public static final int ARRAY_BYTE_INDEX_SCALE;
public static final int ARRAY_SHORT_INDEX_SCALE;
public static final int ARRAY_CHAR_INDEX_SCALE;
public static final int ARRAY_INT_INDEX_SCALE;
public static final int ARRAY_LONG_INDEX_SCALE;
public static final int ARRAY_FLOAT_INDEX_SCALE;
public static final int ARRAY_DOUBLE_INDEX_SCALE;
public static final int ARRAY_OBJECT_INDEX_SCALE;
public static final int ADDRESS_SIZE;

These properties are initialized when the class is loaded, and they are all pointers to arrays of types.

Unsafe static loading


static {
	registerNatives();
	Reflection.registerMethodsToFilter(Unsafe.class, new String[]{"getUnsafe"});
	theUnsafe = new Unsafe();
	ARRAY_BOOLEAN_BASE_OFFSET = theUnsafe.arrayBaseOffset(boolean[].class);
	ARRAY_BYTE_BASE_OFFSET = theUnsafe.arrayBaseOffset(byte[].class);
	ARRAY_SHORT_BASE_OFFSET = theUnsafe.arrayBaseOffset(short[].class);
	ARRAY_CHAR_BASE_OFFSET = theUnsafe.arrayBaseOffset(char[].class);
	ARRAY_INT_BASE_OFFSET = theUnsafe.arrayBaseOffset(int[].class);
	ARRAY_LONG_BASE_OFFSET = theUnsafe.arrayBaseOffset(long[].class);
	ARRAY_FLOAT_BASE_OFFSET = theUnsafe.arrayBaseOffset(float[].class);
	ARRAY_DOUBLE_BASE_OFFSET = theUnsafe.arrayBaseOffset(double[].class);
	ARRAY_OBJECT_BASE_OFFSET = theUnsafe.arrayBaseOffset(Object[].class);
	ARRAY_BOOLEAN_INDEX_SCALE = theUnsafe.arrayIndexScale(boolean[].class);
	ARRAY_BYTE_INDEX_SCALE = theUnsafe.arrayIndexScale(byte[].class);
	ARRAY_SHORT_INDEX_SCALE = theUnsafe.arrayIndexScale(short[].class);
	ARRAY_CHAR_INDEX_SCALE = theUnsafe.arrayIndexScale(char[].class);
	ARRAY_INT_INDEX_SCALE = theUnsafe.arrayIndexScale(int[].class);
	ARRAY_LONG_INDEX_SCALE = theUnsafe.arrayIndexScale(long[].class);
	ARRAY_FLOAT_INDEX_SCALE = theUnsafe.arrayIndexScale(float[].class);
	ARRAY_DOUBLE_INDEX_SCALE = theUnsafe.arrayIndexScale(double[].class);
	ARRAY_OBJECT_INDEX_SCALE = theUnsafe.arrayIndexScale(Object[].class);
	ADDRESS_SIZE = theUnsafe.addressSize();
}
private static native void registerNatives();

Unsafe constructor


private Unsafe() {
}

The Unsafe object cannot pass through new Unsafe () directly, and its constructor is private.

Unsafe instantiation method


public static Unsafe getUnsafe() {
	Class var0 = Reflection.getCallerClass();
	if (!VM.isSystemDomainLoader(var0.getClassLoader())) {
		throw new SecurityException("Unsafe");
	} else {
		return theUnsafe;
	}
}

getUnsafe can only be loaded from the boot class loader (bootstrap class loader), and a direct call to the Unsafe. getUnsafe () method by the non-boot class loader throws an SecurityException exception. Solution:

1. You can make the code "trusted". When running the program, set the bootclasspath option through the JVM parameter, specifying the system classpath plus one Unsafe path to use.


java -Xbootclasspath:/usr/jdk1.7.0/jre/lib/rt.jar:. com.Test

2. Through Java reflex mechanism, violent acquisition.


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

Unsafe Memory Management


//  Gets the size of the local pointer ( Unit is byte) The usual value is 4 Or 8 . Constant ADDRESS_SIZE Is to call this method. 
public native int addressSize();
//  Gets the number of pages in local memory, which is 2 To the power of. 
public native int pageSize();
//  Allocation 1 Block of new local memory, through bytes Specify the size of the memory block ( Unit is byte) Returns the address of the newly created memory. 
public native long allocateMemory(long var1);
//  Through the specified memory address address Resize the local memory block, and the adjusted memory block size is passed through bytes Specify ( Unit is byte) . 
public native long reallocateMemory(long var1, long var3);
//  Sets all bytes in a given memory block to a fixed value ( Usually 0)
public native void setMemory(Object var1, long var2, long var4, byte var6);
//  Memory replication 
public native void copyMemory(Object var1, long var2, Object var4, long var5, long var7);
//  Clear memory 
public native void freeMemory(long var1);

Note: The memory applied by allocateMemory method will be directly separated from jvm, and gc will not be able to manage the memory applied by this method. After using up 1, it is necessary to manually release the memory to prevent memory overflow;
Example in JDK: ByteBuffer. allocateDirect (int capacity) uses DirectByteBuffer, and allocateMemory is used to apply for out-of-heap memory in DirectByteBuffer.

Unsafe Get Offset


//  Returns the memory offset in the class to which the specified variable belongs 
public native long objectFieldOffset(Field var1);
//  Gets the first in the array 1 Address of elements 
public native int arrayBaseOffset(Class<?> var1);
//  Gets the static variable address offset value 
public native long staticFieldOffset(Field var1);
//  In fact, it is the increment of the offset address of the elements in the data, and the addresses of the elements in the array are continuous 
public native int arrayIndexScale(Class<?> var1);

Unsafe Check Class Initialization


//  Detects whether a given class needs initialization. 
//  When ensureClassInitialized Method is returned only when it does not take effect false
public native boolean shouldBeInitialized(Class<?> c);
//  Detects whether the given class has been initialized. 
public native void ensureClassInitialized(Class<?> c);

Unsafe reads from the specified location


private static final Unsafe theUnsafe;
public static final int INVALID_FIELD_OFFSET = -1;
public static final int ARRAY_BOOLEAN_BASE_OFFSET;
public static final int ARRAY_BYTE_BASE_OFFSET;
public static final int ARRAY_SHORT_BASE_OFFSET;
public static final int ARRAY_CHAR_BASE_OFFSET;
public static final int ARRAY_INT_BASE_OFFSET;
public static final int ARRAY_LONG_BASE_OFFSET;
public static final int ARRAY_FLOAT_BASE_OFFSET;
public static final int ARRAY_DOUBLE_BASE_OFFSET;
public static final int ARRAY_OBJECT_BASE_OFFSET;
public static final int ARRAY_BOOLEAN_INDEX_SCALE;
public static final int ARRAY_BYTE_INDEX_SCALE;
public static final int ARRAY_SHORT_INDEX_SCALE;
public static final int ARRAY_CHAR_INDEX_SCALE;
public static final int ARRAY_INT_INDEX_SCALE;
public static final int ARRAY_LONG_INDEX_SCALE;
public static final int ARRAY_FLOAT_INDEX_SCALE;
public static final int ARRAY_DOUBLE_INDEX_SCALE;
public static final int ARRAY_OBJECT_INDEX_SCALE;
public static final int ADDRESS_SIZE;
0

Unsafe writes a value to a specified location


//  Writes to the specified location 1 A int 
public native void putInt(long var1, int var3);
//  Writes to the specified location 1 A char 
public native void putChar(long var1, char var3);
//  Writes to the specified location 1 A byte 
public native void putByte(long var1, byte var3);
//  Writes to the specified location 1 A short 
public native void putShort(long var1, short var3);
//  Writes to the specified location 1 A long 
public native void putLong(long var1, long var3);
//  Writes to the specified location 1 A float 
public native void putFloat(long var1, float var3);
//  Writes to the specified location 1 A double 
public native void putDouble(long var1, double var3);

Unsafe Object Operations

Reads object properties (non-main memory) from the specified offset


public native int getInt(Object var1, long var2);
public native Object getObject(Object var1, long var2);
public native boolean getBoolean(Object var1, long var2);
public native byte getByte(Object var1, long var2);
public native short getShort(Object var1, long var2);
public native char getChar(Object var1, long var2);
public native long getLong(Object var1, long var2);
public native float getFloat(Object var1, long var2);
public native double getDouble(Object var1, long var2);

Modifies object properties (non-main memory) at the specified offset


private static final Unsafe theUnsafe;
public static final int INVALID_FIELD_OFFSET = -1;
public static final int ARRAY_BOOLEAN_BASE_OFFSET;
public static final int ARRAY_BYTE_BASE_OFFSET;
public static final int ARRAY_SHORT_BASE_OFFSET;
public static final int ARRAY_CHAR_BASE_OFFSET;
public static final int ARRAY_INT_BASE_OFFSET;
public static final int ARRAY_LONG_BASE_OFFSET;
public static final int ARRAY_FLOAT_BASE_OFFSET;
public static final int ARRAY_DOUBLE_BASE_OFFSET;
public static final int ARRAY_OBJECT_BASE_OFFSET;
public static final int ARRAY_BOOLEAN_INDEX_SCALE;
public static final int ARRAY_BYTE_INDEX_SCALE;
public static final int ARRAY_SHORT_INDEX_SCALE;
public static final int ARRAY_CHAR_INDEX_SCALE;
public static final int ARRAY_INT_INDEX_SCALE;
public static final int ARRAY_LONG_INDEX_SCALE;
public static final int ARRAY_FLOAT_INDEX_SCALE;
public static final int ARRAY_DOUBLE_INDEX_SCALE;
public static final int ARRAY_OBJECT_INDEX_SCALE;
public static final int ADDRESS_SIZE;
3

Modifies object properties (main memory) at the specified offset


private static final Unsafe theUnsafe;
public static final int INVALID_FIELD_OFFSET = -1;
public static final int ARRAY_BOOLEAN_BASE_OFFSET;
public static final int ARRAY_BYTE_BASE_OFFSET;
public static final int ARRAY_SHORT_BASE_OFFSET;
public static final int ARRAY_CHAR_BASE_OFFSET;
public static final int ARRAY_INT_BASE_OFFSET;
public static final int ARRAY_LONG_BASE_OFFSET;
public static final int ARRAY_FLOAT_BASE_OFFSET;
public static final int ARRAY_DOUBLE_BASE_OFFSET;
public static final int ARRAY_OBJECT_BASE_OFFSET;
public static final int ARRAY_BOOLEAN_INDEX_SCALE;
public static final int ARRAY_BYTE_INDEX_SCALE;
public static final int ARRAY_SHORT_INDEX_SCALE;
public static final int ARRAY_CHAR_INDEX_SCALE;
public static final int ARRAY_INT_INDEX_SCALE;
public static final int ARRAY_LONG_INDEX_SCALE;
public static final int ARRAY_FLOAT_INDEX_SCALE;
public static final int ARRAY_DOUBLE_INDEX_SCALE;
public static final int ARRAY_OBJECT_INDEX_SCALE;
public static final int ADDRESS_SIZE;
4

Modifies object properties (main memory) at the specified offset


private static final Unsafe theUnsafe;
public static final int INVALID_FIELD_OFFSET = -1;
public static final int ARRAY_BOOLEAN_BASE_OFFSET;
public static final int ARRAY_BYTE_BASE_OFFSET;
public static final int ARRAY_SHORT_BASE_OFFSET;
public static final int ARRAY_CHAR_BASE_OFFSET;
public static final int ARRAY_INT_BASE_OFFSET;
public static final int ARRAY_LONG_BASE_OFFSET;
public static final int ARRAY_FLOAT_BASE_OFFSET;
public static final int ARRAY_DOUBLE_BASE_OFFSET;
public static final int ARRAY_OBJECT_BASE_OFFSET;
public static final int ARRAY_BOOLEAN_INDEX_SCALE;
public static final int ARRAY_BYTE_INDEX_SCALE;
public static final int ARRAY_SHORT_INDEX_SCALE;
public static final int ARRAY_CHAR_INDEX_SCALE;
public static final int ARRAY_INT_INDEX_SCALE;
public static final int ARRAY_LONG_INDEX_SCALE;
public static final int ARRAY_FLOAT_INDEX_SCALE;
public static final int ARRAY_DOUBLE_INDEX_SCALE;
public static final int ARRAY_OBJECT_INDEX_SCALE;
public static final int ADDRESS_SIZE;
5

Unsafe CAS Operation


private static final Unsafe theUnsafe;
public static final int INVALID_FIELD_OFFSET = -1;
public static final int ARRAY_BOOLEAN_BASE_OFFSET;
public static final int ARRAY_BYTE_BASE_OFFSET;
public static final int ARRAY_SHORT_BASE_OFFSET;
public static final int ARRAY_CHAR_BASE_OFFSET;
public static final int ARRAY_INT_BASE_OFFSET;
public static final int ARRAY_LONG_BASE_OFFSET;
public static final int ARRAY_FLOAT_BASE_OFFSET;
public static final int ARRAY_DOUBLE_BASE_OFFSET;
public static final int ARRAY_OBJECT_BASE_OFFSET;
public static final int ARRAY_BOOLEAN_INDEX_SCALE;
public static final int ARRAY_BYTE_INDEX_SCALE;
public static final int ARRAY_SHORT_INDEX_SCALE;
public static final int ARRAY_CHAR_INDEX_SCALE;
public static final int ARRAY_INT_INDEX_SCALE;
public static final int ARRAY_LONG_INDEX_SCALE;
public static final int ARRAY_FLOAT_INDEX_SCALE;
public static final int ARRAY_DOUBLE_INDEX_SCALE;
public static final int ARRAY_OBJECT_INDEX_SCALE;
public static final int ADDRESS_SIZE;
6

The CAS operation is carried out for the object, and the attribute of the specified offset in the object is essentially updated. When the original value is var4, it will be updated to var5 and return true, otherwise it will return false.
Example: volatile i=0; There are multiple threads that modify the value of i, and the A thread only modifies to 2 when i=1, if the code is as follows


if (i == 1) {i = 2;} 

If there is a problem in this way, if may have been modified by others after comparing i. This scenario is especially suitable for CAS. Use CAS code as follows


boolean isUpdate =  compareAndSwapInt(object, offset, 1, 2)

Equivalent to reading- > Judge- > Write it once (I really can't understand CAS, I can understand it this way)

Suspend and Resume of Unsafe Threads


private static final Unsafe theUnsafe;
public static final int INVALID_FIELD_OFFSET = -1;
public static final int ARRAY_BOOLEAN_BASE_OFFSET;
public static final int ARRAY_BYTE_BASE_OFFSET;
public static final int ARRAY_SHORT_BASE_OFFSET;
public static final int ARRAY_CHAR_BASE_OFFSET;
public static final int ARRAY_INT_BASE_OFFSET;
public static final int ARRAY_LONG_BASE_OFFSET;
public static final int ARRAY_FLOAT_BASE_OFFSET;
public static final int ARRAY_DOUBLE_BASE_OFFSET;
public static final int ARRAY_OBJECT_BASE_OFFSET;
public static final int ARRAY_BOOLEAN_INDEX_SCALE;
public static final int ARRAY_BYTE_INDEX_SCALE;
public static final int ARRAY_SHORT_INDEX_SCALE;
public static final int ARRAY_CHAR_INDEX_SCALE;
public static final int ARRAY_INT_INDEX_SCALE;
public static final int ARRAY_LONG_INDEX_SCALE;
public static final int ARRAY_FLOAT_INDEX_SCALE;
public static final int ARRAY_DOUBLE_INDEX_SCALE;
public static final int ARRAY_OBJECT_INDEX_SCALE;
public static final int ADDRESS_SIZE;
9

Blocks the current thread until one unpark method appears (called), one method for unpark has already appeared (called before this park method call), the thread is interrupted, or the time time expires (that is, the blocking timeout). In the case of non-zero time, if isAbsolute is true, time is relative to milliseconds after the new era, otherwise time is nanosecond. This method may also return unreasonably when executed (for no specific reason). The framework suspending operations on threads in java. util. concurrent are encapsulated in the LockSupport class. There are various versions of pack methods in the LockSupport class, but they all call the Unsafe # park () method in the end.


public native void unpark(Object var1);

Releases the block created by park on 1 thread. This method can also be used to terminate a block caused by a previous call to park. This operation is not safe, so the thread must be guaranteed to be alive (thread has not been destroyed). It is obvious to judge whether a thread survives from Java code, but this opportunity cannot be automatically completed from native code.

Unsafe Memory Barrier


public native void loadFence();

All read operations prior to this method, 1 must be performed before the load barrier.


public native void storeFence();

All write operations before this method, 1 must be completed before the store barrier


public native void fullFence();

All read and write operations prior to this method are performed before the full barrier, which is equivalent to the combined function of the above two (load barrier and store barrier).

Unsafe Others


public native int getLoadAverage(double[] loadavg, int nelems);

Get the average load value of the system. loadavg, an double array, will store the result of the load value. nelems determines the number of samples, and nelems can only take the value of 1 to 3, representing the average load of the system in the last 1, 5 and 15 minutes respectively. If the payload of the system cannot be obtained, this method returns-1, otherwise, the number of samples obtained (the number of valid elements in loadavg). In the experiment, this method 1 returns-1 directly. In fact, the related method in JMX can be used instead of this method.


public native void throwException(Throwable ee);

Throw an exception directly by bypassing the detection mechanism.


Related articles: