Explanation on the Use of Unsafe in java

  • 2021-12-12 04:18:59
  • OfStack

Directory 1. Get unsafe2. Get unsafe

Some time ago, because I looked at the source code of JUC, there are a lot of operations about unsafe, so I came to see it. Write some notes to summarize (this article is based on jdk 1.8):

unsafe can help us operate hardware resources directly. Of course, it is carried out with the help of jit of java. It is not officially recommended because it is unsafe. For example, you use unsafe to create a super-large array, but this array jvm is not managed and can only be operated by yourself. It is easy for oom and is not conducive to resource recovery.

Ok, let's look at the code

1. Get unsafe


//1. The simplest way to use it is to obtain it based on reflection Unsafe Instances 
Field f = Unsafe.class.getDeclaredField("theUnsafe");
f.setAccessible(true);
Unsafe unsafe = (Unsafe) f.get(null);

2. Get unsafe


private static Unsafe unsafe = null;
private static Field getUnsafe = null; 
static {
    try {
        getUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
        getUnsafe.setAccessible(true);
        unsafe = (Unsafe) getUnsafe.get(null);
    } catch (NoSuchFieldException e) {
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    }
}

You can get unfase as long as you are happy, because it involves the permission problem of unfase, so we can only get it in this way, otherwise it is abnormal permission.

Operation method:


/**
 *  Operational array :
 *  You can get the basic offset of the array in the content ( arrayBaseOffset ), get the interval (scale) of the elements in the array, 
 *  Gets the element value from the array object and offset ( getObject ), setting the array element values ( putObject ), as shown below. 
 */
String[] strings = new String[]{"1", "2", "3"};
long i = unsafe.arrayBaseOffset(String[].class);
System.out.println("string[] base offset is :" + i);
 
//every index scale
long scale = unsafe.arrayIndexScale(String[].class);
System.out.println("string[] index scale is " + scale);
 
//print first string in strings[]
System.out.println("first element is :" + unsafe.getObject(strings, i));
 
//set 100 to first string
unsafe.putObject(strings, i + scale * 0, "100");
 
//print first string in strings[] again
System.out.println("after set ,first element is :" + unsafe.getObject(strings, i + scale * 0));

/**
 *  Object operation 
 *  Instantiation Data
 *
 *  You can pass the class's class Object creates a class object ( allocateInstance ), get the offset of the object property ( objectFieldOffset ) 
 *  That sets the value of the object by offset ( putObject ) 
 *
 *  Deserialization of objects 
 *  When using a framework to deserialize or build an object, the assumption is that it is rebuilt from an existing object, and you expect to use reflection to call the class's setup function, 
 *  Or more accurately 1 Point can directly set internal fields or even final The function of the field. The problem is that you want to create 1 An instance of an object, 
 *  But you don't actually need a constructor because it can make the problem more difficult and have side effects. 
 *
 */
// Call allocateInstance Function avoids calling the constructor when we don't need it 
Data data = (Data) unsafe.allocateInstance(Data.class);
data.setId(1L);
data.setName("unsafe");
System.out.println(data);
 
// Returns the offset of the in-memory address of a member property relative to the object memory address 
Field nameField = Data.class.getDeclaredField("name");
long fieldOffset = unsafe.objectFieldOffset(nameField);
//putLong , putInt , putDouble , putChar , putObject And so on, directly modify the memory data (you can bypass the access rights) 
unsafe.putObject(data,fieldOffset," This is the new value ");
System.out.println(data.getName());
  
/**
 *  We can create at run time 1 Class, such as from the compiled .class In the file. Reads the contents of the class as an array of bytes, 
 *  And correctly passed to defineClass Methods; When you have to create a class dynamically, and the existing code has 1 Some agents,   This is very useful 
 */
File file = new File("C:\\workspace\\idea2\\disruptor\\target\\classes\\com\\onyx\\distruptor\\test\\Data.class");
FileInputStream input = new FileInputStream(file);
byte[] content = new byte[(int)file.length()];
input.read(content);
Class c = unsafe.defineClass(null, content, 0, content.length,null,null);
c.getMethod("getId").invoke(c.newInstance(), null);
   
/**
 *  Memory operation 
 *  Can be found in Java Memory is allocated in the memory area ( allocateMemory ), set the memory ( setMemory For initialization, 
 *  Sets the value in the specified memory location ( putInt\putBoolean\putDouble Such as basic types) 
 */
// Allocation 1 A 8byte Memory of 
long address = unsafe.allocateMemory(8L);
// Initialize memory fill 1
unsafe.setMemory(address, 8L, (byte) 1);
// Test output 
System.out.println("add byte to memory:" + unsafe.getInt(address));
// Settings 0-3 4 A byte For 0x7fffffff
unsafe.putInt(address, 0x7fffffff);
// Settings 4-7 4 A byte For 0x80000000
unsafe.putInt(address + 4, 0x80000000);
//int Occupation 4byte
System.out.println("add byte to memory:" + unsafe.getInt(address));
System.out.println("add byte to memory:" + unsafe.getInt(address + 4));

/**
 * CAS Operation 
 * Compare And Swap (Compare and swap) When the value to be changed is the desired value, it is replaced with a new value, which is atomic 
 *  (Can't be divided) operation. Many concurrency frameworks are used at the bottom CAS Operation, CAS The advantage of operation is that there is no lock, which can reduce the cost of thread switching 
 *  Time, but CAS Frequent failure to run is easy to cause performance problems, and there are also ABA Problem. In Unsafe Include in compareAndSwapObject , 
 * compareAndSwapInt , compareAndSwapLong3 Methods, compareAndSwapInt A simple example of. 
 */
Data data = new Data();
data.setId(1L);
Field id = data.getClass().getDeclaredField("id");
long l = unsafe.objectFieldOffset(id);
id.setAccessible(true);
// Compare and exchange, such as id If the value of is the expected value 1 Is replaced by 2 Otherwise, it will not be processed 
unsafe.compareAndSwapLong(data,1L,1L,2L);
System.out.println(data.getId());

/**
 *  Constant acquisition 
 *
 *  You can get the address size ( addressSize ), page size ( pageSize ), the offset of the primitive type array 
 *  ( Unsafe.ARRAY_INT_BASE_OFFSET\Unsafe.ARRAY_BOOLEAN_BASE_OFFSET Etc.), 
 *  The interval between elements in the primitive type array ( Unsafe.ARRAY_INT_INDEX_SCALE\Unsafe.ARRAY_BOOLEAN_INDEX_SCALE Etc.) 
 */
//get os address size
System.out.println("address size is :" + unsafe.addressSize());
//get os page size
System.out.println("page size is :" + unsafe.pageSize());
//int array base offset
System.out.println("unsafe array int base offset:" + Unsafe.ARRAY_INT_BASE_OFFSET);
   
/**
 *  Thread permission 
 *  Permit threads to pass ( park ), or let the thread wait for permission (unpark) , 
 */
Thread packThread = new Thread(() -> {
    long startTime = System.currentTimeMillis();
    // Nanosecond, relative time park
    unsafe.park(false,3000000000L);
    // Milliseconds, absolute time park
    //unsafe.park(true,System.currentTimeMillis()+3000); 
    System.out.println("main thread end,cost :"+(System.currentTimeMillis()-startTime)+"ms");
});
packThread.start();
TimeUnit.SECONDS.sleep(1);
// Comment dropped 1 After the line, the thread 3 Output after seconds , Otherwise, in 1 Output after seconds 
unsafe.unpark(packThread);

/**
 * Java The maximum array size is Integer.MAX_VALUE . With direct memory allocation, the array size we create is limited to the heap size; 
 *  In fact, this is out-of-heap memory ( off-heap memory ) Technology, in java.nio Part of the package is available; 
 *
 *  Memory allocation in this way is not on the heap and is not affected by GC Management, so you must be careful Unsafe.freeMemory() The use of. 
 *  It also does not perform any boundary checks, so any illegal access may result in JVM Collapse 
 */
public class SuperArray { 
    private static Unsafe unsafe = null;
    private static Field getUnsafe = null; 
    static {
        try {
            getUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
            getUnsafe.setAccessible(true);
            unsafe = (Unsafe) getUnsafe.get(null);
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    } 
 
    private final static int BYTE = 1; 
    private long size;
    private long address; 
    public SuperArray(long size) {
        this.size = size;
        address = unsafe.allocateMemory(size * BYTE);
    }
 
    public void set(long i, byte value) {
        unsafe.putByte(address + i * BYTE, value);
    }
 
    public int get(long idx) {
        return unsafe.getByte(address + idx * BYTE);
    }
 
    public long size() {
        return size;
    } 
 
    public static void main(String[] args) {
        long SUPER_SIZE = (long)Integer.MAX_VALUE * 2;
        SuperArray array = new SuperArray(SUPER_SIZE);
        System.out.println("Array size:" + array.size()); // 4294967294
        int sum=0;
        for (int i = 0; i < 100; i++) {
            array.set((long)Integer.MAX_VALUE + i, (byte)3);
            sum += array.get((long)Integer.MAX_VALUE + i);
        }
        System.out.println(sum);
    } 
}

Related articles: