Java multi-threading series --sun.misc.Unsafe understanding

Foreword

Unsafe class under sun.misc package, not part of the Java standard. But many Java foundation class library, including some high-performance development libraries are widely used are based on the Unsafe class development, such as Netty, Hadoop, Kafka and so on.

Unsafe use system memory may be used to directly access resources and self-management, played an important role in improving the Unsafe class Java operating efficiency, enhance operational capability underlying Java language.

Unsafe Java can be considered to be left in the back door, it offers some low-level operations, such as direct memory access, thread scheduling.

 The official does not recommend the use of Unsafe.

 The use of class

 Gets Unsafe Objects

Unfortunately, Unsafe object can not be directly new Unsafe()or calling Unsafe.getUnsafe()obtain the following reasons:

* Not directly new Unsafe(), because Unsafeis designed to be single-mode embodiment, the constructor is private;

* Not by callingUnsafe.getUnsafe()获取,因为getUnsafe被设计成只能从引导类加载器(bootstrap class loader)加载,从getUnsafe的源码中也可以看出来,如下:

    @CallerSensitive
    public static Unsafe getUnsafe() {
        //得到调用该方法的Class对象
        Class cc = Reflection.getCallerClass();
        //判断调用该方法的类是否是引导类加载器(bootstrap class loader)
        //如果不是的话,比如由AppClassLoader调用该方法,则抛出SecurityException异常
        if (cc.getClassLoader() != null)
            throw new SecurityException("Unsafe");
        //返回单例对象
        return theUnsafe;
    }

While we can not be obtained by the above method Unsafe objects, but have some Private Unsafe class has a static global properties theUnsafe(Unsafe实例对象), by reflection, the object can be acquired Field attribute theUnsafe the member corresponding to, and arranged to be accessible, particularly to obtain theUnsafe the object, the following code: 

package concurrency;

import java.lang.reflect.Field;
import sun.misc.Unsafe;
import sun.reflect.Reflection;

public class Test {
    public static void main(String[] args) throws NoSuchFieldException,
            SecurityException, IllegalArgumentException, IllegalAccessException {
        // 通过反射得到theUnsafe对应的Field对象
        Field field = Unsafe.class.getDeclaredField("theUnsafe");
        // 设置该Field为可访问
        field.setAccessible(true);
        // 通过Field得到该Field对应的具体对象,传入null是因为该Field为static的
        Unsafe unsafe = (Unsafe) field.get(null);
        System.out.println(unsafe);

    }
}

Unsafe class source code analysis 

Most of the API is native Unsafe methods to achieve by calling JNI code (JNI: Java Native Interface for the JAVA local call), including the following categories:

1) Class related. The main provider of Class and method of operation of its static fields.

2) Object-related. Object The main methods of operation and provides its fields.

3) Arrray related. The method of operation and provides the main array elements.

4) concurrent relevant. Mainly provides low-level synchronization primitives, such as CAS, thread scheduling, volatile, memory barriers.

5) Memory-related. Provides a direct memory access method (direct operation of the local bypass the Java heap memory), you can do the same as C as free use of system memory resources.

6) system-related. The main memory is returned some low-level information, such as address size, memory page size.

A class to facilitate the subsequent Examples 

public class VO
{
	public int a = 0;
	
	public long b = 0;
	
	public static String c= "123";
	
	public static Object d= null;
	
	public static int e = 100;
}

1.Class related 

//静态属性的偏移量,用于在对应的Class对象中读写静态属性
public native long staticFieldOffset(Field f);
  
public native Object staticFieldBase(Field f);
//判断是否需要初始化一个类
public native boolean shouldBeInitialized(Class<?> c);
//确保类被初始化
public native void ensureClassInitialized(Class<?> c);
//定义一个类,可用于动态创建类
public native Class<?> defineClass(String name, byte[] b, int off, int len,
     ClassLoader loader,
     ProtectionDomain protectionDomain);
//定义一个匿名类,可用于动态创建类
public native Class<?> defineAnonymousClass(Class<?> hostClass, byte[] data, Object[] cpPatches);

Gets the property value of a static field 

VO.e = 1024;
Field sField = VO.class.getDeclaredField("e");
Object base = unsafe.staticFieldBase(sField);
long offset = unsafe.staticFieldOffset(sField);
System.out.println(unsafe.getInt(base, offset));//1024

 2.Object related

//获得对象的字段偏移量 
public native long objectFieldOffset(Field f); 
//获得给定对象地址偏移量的int值
public native int getInt(Object o, long offset);
//设置给定对象地址偏移量的int值
public native void putInt(Object o, long offset, int x);

//创建对象,但并不会调用其构造方法。如果类未被初始化,将初始化类。
public native Object allocateInstance(Class<?> cls)
 throws InstantiationException;

Reading the value of the field object instance 

//获取实例字段的属性值
VO vo = new VO();
vo.a = 10000;
long aoffset = unsafe.objectFieldOffset(VO.class.getDeclaredField("a"));
int va = unsafe.getInt(vo, aoffset);
System.out.println("va="+va);

3. Array related 

/**
 * Report the offset of the first element in the storage allocation of a
 * given array class. If {@link #arrayIndexScale} returns a non-zero value
 * for the same class, you may use that scale factor, together with this
 * base offset, to form new offsets to access elements of arrays of the
 * given class.
 *
 * @see #getInt(Object, long)
 * @see #putInt(Object, long, int)
 */
//返回数组中第一个元素的偏移地址
public native int arrayBaseOffset(Class<?> arrayClass);
//boolean、byte、short、char、int、long、float、double,及对象类型均有以下方法
/** The value of {@code arrayBaseOffset(boolean[].class)} */
public static final int ARRAY_BOOLEAN_BASE_OFFSET
 = theUnsafe.arrayBaseOffset(boolean[].class);
  
/**
 * Report the scale factor for addressing elements in the storage
 * allocation of a given array class. However, arrays of "narrow" types
 * will generally not work properly with accessors like {@link
 * #getByte(Object, int)}, so the scale factor for such classes is reported
 * as zero.
 *
 * @see #arrayBaseOffset
 * @see #getInt(Object, long)
 * @see #putInt(Object, long, int)
 */
//返回数组中每一个元素占用的大小
public native int arrayIndexScale(Class<?> arrayClass);
  
//boolean、byte、short、char、int、long、float、double,及对象类型均有以下方法
/** The value of {@code arrayIndexScale(boolean[].class)} */
public static final int ARRAY_BOOLEAN_INDEX_SCALE
 = theUnsafe.arrayIndexScale(boolean[].class);

Get head size and element size of the array 

// 数组第一个元素的偏移地址,即数组头占用的字节数
int[] intarr = new int[0];
System.out.println(unsafe.arrayBaseOffset(intarr.getClass()));
 
// 数组中每个元素占用的大小
System.out.println(unsafe.arrayIndexScale(intarr.getClass()));

ArrayBaseOffset and positionable by arrayIndexScale each element position in the array in memory. 

4. Concurrent related 

4.1CAS related

CAS: CompareAndSwap, memory offset address offset, the expected value expected, the new value of x. If the value is equal to the expected, try to update the value of the variable x in the expected value and the current time. If the update is successful, it returns true; otherwise, returns false.

//更新变量值为x,如果当前值为expected
//o:对象 offset:偏移量 expected:期望值 x:新值
public final native boolean compareAndSwapObject(Object o, long offset,
       Object expected,
       Object x);
  
public final native boolean compareAndSwapInt(Object o, long offset,
      int expected,
      int x);
  
public final native boolean compareAndSwapLong(Object o, long offset,
      long expected,
      long x);

 Starting with Java 8, Unsafe provides the following methods:

//增加
public final int getAndAddInt(Object o, long offset, int delta) {
 int v;
 do {
 v = getIntVolatile(o, offset);
 } while (!compareAndSwapInt(o, offset, v, v + delta));
 return v;
}
  
public final long getAndAddLong(Object o, long offset, long delta) {
 long v;
 do {
 v = getLongVolatile(o, offset);
 } while (!compareAndSwapLong(o, offset, v, v + delta));
 return v;
}
//设置
public final int getAndSetInt(Object o, long offset, int newValue) {
 int v;
 do {
 v = getIntVolatile(o, offset);
 } while (!compareAndSwapInt(o, offset, v, newValue));
 return v;
}
  
public final long getAndSetLong(Object o, long offset, long newValue) {
 long v;
 do {
 v = getLongVolatile(o, offset);
 } while (!compareAndSwapLong(o, offset, v, newValue));
 return v;
}
  
public final Object getAndSetObject(Object o, long offset, Object newValue) {
 Object v;
 do {
 v = getObjectVolatile(o, offset);
 } while (!compareAndSwapObject(o, offset, v, newValue));
 return v;

4.2 thread scheduling related 

//取消阻塞线程
public native void unpark(Object thread);
//阻塞线程
public native void park(boolean isAbsolute, long time);
//获得对象锁
public native void monitorEnter(Object o);
//释放对象锁
public native void monitorExit(Object o);
//尝试获取对象锁,返回true或false表示是否获取成功
public native boolean tryMonitorEnter(Object o);

 4.3volatile related to reading and writing

Basic types (boolean, byte, char, short, int, long, float, double) Java and object reference in the type has the following methods.

//从对象的指定偏移量处获取变量的引用,使用volatile的加载语义
//相当于getObject(Object, long)的volatile版本
public native Object getObjectVolatile(Object o, long offset);
  
//存储变量的引用到对象的指定的偏移量处,使用volatile的存储语义
//相当于putObject(Object, long, Object)的volatile版本
public native void putObjectVolatile(Object o, long offset, Object x);



/**
 * Version of {@link #putObjectVolatile(Object, long, Object)}
 * that does not guarantee immediate visibility of the store to
 * other threads. This method is generally only useful if the
 * underlying field is a Java volatile (or if an array cell, one
 * that is otherwise only accessed using volatile accesses).
 */
public native void putOrderedObject(Object o, long offset, Object x);
  
/** Ordered/Lazy version of {@link #putIntVolatile(Object, long, int)} */
public native void putOrderedInt(Object o, long offset, int x);
  
/** Ordered/Lazy version of {@link #putLongVolatile(Object, long, long)} */
public native void putOrderedLong(Object o, long offset, long x);

4.4 memory-related barriers

Java 8 is introduced for defining memory barriers, to avoid the code reordering.

//内存屏障,禁止load操作重排序,即屏障前的load操作不能被重排序到屏障后,屏障后的load操作不能被重排序到屏障前
public native void loadFence();
//内存屏障,禁止store操作重排序,即屏障前的store操作不能被重排序到屏障后,屏障后的store操作不能被重排序到屏障前
public native void storeFence();
//内存屏障,禁止load、store操作重排序
public native void fullFence();

5. Direct Memory Access (non-heap memory)

allocateMemory allocated memory manually free (not collected GC)

//(boolean、byte、char、short、int、long、float、double)都有以下get、put两个方法。 
//获得给定地址上的int值
public native int getInt(long address);
//设置给定地址上的int值
public native void putInt(long address, int x);
//获得本地指针
public native long getAddress(long address);
//存储本地指针到给定的内存地址
public native void putAddress(long address, long x);
  
//分配内存
public native long allocateMemory(long bytes);
//重新分配内存
public native long reallocateMemory(long address, long bytes);
//初始化内存内容
public native void setMemory(Object o, long offset, long bytes, byte value);
//初始化内存内容
public void setMemory(long address, long bytes, byte value) {
 setMemory(null, address, bytes, value);
}
//内存内容拷贝
public native void copyMemory(Object srcBase, long srcOffset,
    Object destBase, long destOffset,
    long bytes);
//内存内容拷贝
public void copyMemory(long srcAddress, long destAddress, long bytes) {
 copyMemory(null, srcAddress, null, destAddress, bytes);
}
//释放内存
public native void freeMemory(long address);

 6. system-related

//返回指针的大小。返回值为4或8。
public native int addressSize();
  
/** The value of {@code addressSize()} */
public static final int ADDRESS_SIZE = theUnsafe.addressSize();
  
//内存页的大小。
public native int pageSize();

Knowledge extension

We note that there is a method of the above

  • stateOffset = unsafe.objectFieldOffset (field) can be understood from the name of the method: the offset of the acquired object property Field of the object.

To understand this offset, you need to understand the java memory model

Java Memory Model

 

Java objects stored in the memory layout can be divided into three areas: object header (Header), instance data (Instance Data) and alignment padding (Padding), simple understanding:

  • Object header , what an object is?
  • Instance data , what objects there?
  • Alignment padding , is not critical, the purpose is to achieve a multiple of 8 bits padded.

As a simple example, the following categories:

class VO {
    public int a = 0;
    public int b = 0;
}

 VO vo = new VO (); when, Java opened to a memory address, the object header contains a fixed length (assumed to be 16 bytes, bits are different machines / object header compression will affect the object header length) + examples of data (a + 4 bytes b 4 bytes) + padding.

Said direct conclusion here, we said above, the offset is embodied here as a property of the offset is above 16, the offset is 20 b properties.

In unsafe inside the class, we have found a method unsafe.getInt (object, offset);
by unsafe.getInt (vo, 16) can be obtained in vo.a value. Is not think reflected? In fact, reflecting the underlying java is used UNSAFE

Further reflection

How do I know which offset a class for each property? Only the offset amount, java how do you know where to read so far is the value of this property?

View property offset recommend a tool like jol: http://openjdk.java.net/projects/code-tools/jol/
with jol can easily view the memory layout of java, combined with the code explain

public class VO {
    public int a = 0;
    public long b = 0;
    public String c= "123";
    public Object d= null;
    public int e = 100;
    public static int f= 0;
    public static String g= "";
    public Object h= null;
    public boolean i;
}
public static void main(String[] args) throws Exception {
        System.out.println(VM.current().details());
        System.out.println(ClassLayout.parseClass(VO.class).toPrintable());
        System.out.println("=================");
        Unsafe unsafe = getUnsafeInstance();
        VO vo = new VO();
        vo.a=2;
        vo.b=3;
        vo.d=new HashMap<>();
        long aoffset = unsafe.objectFieldOffset(VO.class.getDeclaredField("a"));
        System.out.println("aoffset="+aoffset);
        // 获取a的值
        int va = unsafe.getInt(vo, aoffset);
        System.out.println("va="+va);
    }

    public static Unsafe getUnsafeInstance() throws Exception {
        // 通过反射获取rt.jar下的Unsafe类
        Field theUnsafeInstance = Unsafe.class.getDeclaredField("theUnsafe");
        theUnsafeInstance.setAccessible(true);
        // return (Unsafe) theUnsafeInstance.get(null);是等价的
        return (Unsafe) theUnsafeInstance.get(Unsafe.class);
    }

In my local machine test results are as follows: 

# Running 64-bit HotSpot VM.
# Using compressed oop with 0-bit shift.
# Using compressed klass with 3-bit shift.
# Objects are 8 bytes aligned.
# Field sizes by type: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]
# Array element sizes: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]

com.ha.net.nsp.product.VO object internals:
 OFFSET  SIZE               TYPE DESCRIPTION                               VALUE
      0    12                    (object header)                           N/A
     12     4                int VO.a                                      N/A
     16     8               long VO.b                                      N/A
     24     4                int VO.e                                      N/A
     28     1            boolean VO.i                                      N/A
     29     3                    (alignment/padding gap)                  
     32     4   java.lang.String VO.c                                      N/A
     36     4   java.lang.Object VO.d                                      N/A
     40     4   java.lang.Object VO.h                                      N/A
     44     4                    (loss due to the next object alignment)
Instance size: 48 bytes
Space losses: 3 bytes internal + 4 bytes external = 7 bytes total

=================
aoffset=12
va=2

 In the results, we found that:

  • 1, my local virtual machine environment is open and the compressed 64-bit compression, objects are 8-byte alignment
  • 2, VO class memory layout object header contains a 12-byte, 4-byte int data, data of 8 bytes long, and the other String Object is 4 bytes, 4 bytes finally aligned.
  • 3, the order is inconsistent memory layout VO class property with the property declaration.
  • 4, static attributes of the class VO VO memory layout is not, because he belongs to the class class.
  • 5, VO class can be determined by the number of bytes occupied by an object, this space has been determined at compile time (Note: This space is not a real space object).
  • 6, as described above, 12 can be read by an offset value 2 is stored here.

Come out of new problems:
1, where the object header is 12 bytes Why? What are the specific objects in advance contain?
A: Under normal circumstances, an object occupying a first machine code is 8 bytes in the 32-bit system, the system 64 is a machine code occupies 16 bytes. But in my local environment it is to open a reference (pointer) compression, so only 12 bytes.
2, here's String and Object Why is 4 bytes?
A: Because the String or Object type, memory layout, are reference types, so his size with about whether to start the compression. When the compression is not started, the machine 32 is a type of reference 4 bytes, 8 bytes is 64, but if the compression start, the machine 64 becomes a reference type 4 bytes.
3, Java should know how to read from 12 to offset 16 offset it, instead of reading the offset to 18 or 20?
A: Here, I guess, the virtual machine at compile time, it has retained the offset array VO class that offset of 12 is 16, so Java read up until 16.

Note: For normal use: 

public class Test implements java.io.Serializable {
    //序列化相关
    private static final long serialVersionUID = 6214790243416807050L;

    // JDK里使用的一个工具类对象,提供一些不安全的操作的方法,一般不会在自己的程序中使用该类
    //在这里主要用到其中的objectFieldOffset、putOrderedInt、compareAndSwapInt方法
    private static final Unsafe unsafe = Unsafe.getUnsafe();
    
    //value成员属性的内存地址相对于对象内存地址的偏移量
    private static final long valueOffset;

    static {
      try {
        //初始化valueOffset,通过unsafe.objectFieldOffset方法获取成员属性value内存地址相对于对象内存地址的偏移量
        valueOffset = unsafe.objectFieldOffset(AtomicInteger.class.getDeclaredField("value"));
      } catch (Exception ex) { throw new Error(ex); }
    }
    //int的值,设为volatile,保证线程之间的可见性
    private volatile int value;

    ......
}

 

Published 136 original articles · won praise 6 · views 1529

Guess you like

Origin blog.csdn.net/weixin_42073629/article/details/104489155