Unsafe

2017-01-12  本文已影响0人  风干鸡

关于Unsafe

如何获取Unsafe实例

通过Unsafe我们能做些什么

CGLIB-based proxy classes no longer require a default constructor. Support is provided via the objenesis library which is repackaged inline and distributed as part of the Spring Framework. With this strategy, no constructor at all is being invoked for proxy instances anymore.
3. New Features and Enhancements in Spring Framework 4.0

public class Test {
        static Unsafe unsafe ;
        static int i = 1;
        static {
            Field theUnsafe = null;
            try {
                theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
            } catch (NoSuchFieldException e) {
                e.printStackTrace();
            }
            theUnsafe.setAccessible(true);
            try {
                unsafe = (Unsafe) theUnsafe.get(null);
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
    
        private Test() {
    
        }
    
        public static void main(String[] args){
            try {
                long i = unsafe.staticFieldOffset(Test.class.getDeclaredField("i"));
                System.out.println(unsafe.getInt(new Test(), i));
            } catch (NoSuchFieldException e) {
                e.printStackTrace();
            }
        }
    }

再提一个无关的事情,我在很多地方看到说类的Static变量是存在方法区中的,而据我观察并不是这样。
比如访问类变量,unsafe.get接收的第一个参数是Class对象,而这个对象并不是方法区中的InstanceKlass,所以我猜Static变量应该是接在Class对象的尾部。
我们用jol来打印Class对象,(这个库其实也是用Unsafe+反射实现的)

import org.openjdk.jol.info.ClassLayout;
import sun.misc.Unsafe;
import java.lang.reflect.Field;
public class Test {

  static Unsafe unsafe;
  static {
      Field f = null;
      try {
          f = Unsafe.class.getDeclaredField("theUnsafe");
      } catch (NoSuchFieldException e) {
          e.printStackTrace();
      }

      f.setAccessible(true);

      try {
          unsafe = (Unsafe) f.get(null);
      } catch (IllegalAccessException e) {
          e.printStackTrace();
      }
  }

  public static void main(String[] args) throws NoSuchFieldException {
      System.out.println(ClassLayout.parseClass(Class.class).toPrintable());
      System.out.println(unsafe.staticFieldOffset(Test.class.getDeclaredField("unsafe")));
  }
}

输出:

java.lang.Class object internals:
     OFFSET  SIZE            TYPE DESCRIPTION                    VALUE
          0    12                 (object header)                N/A
         12     4     Constructor Class.cachedConstructor        N/A
         16     4           Class Class.newInstanceCallerCache   N/A
         20     4          String Class.name                     N/A
         24     4                 (alignment/padding gap)        N/A
         28     4   SoftReference Class.reflectionData           N/A
         32     4 ClassRepository Class.genericInfo              N/A
         36     4        Object[] Class.enumConstants            N/A
         40     4             Map Class.enumConstantDirectory    N/A
         44     4  AnnotationData Class.annotationData           N/A
         48     4  AnnotationType Class.annotationType           N/A
         52     4   ClassValueMap Class.classValueMap            N/A
         56    32                 (alignment/padding gap)        N/A
         88     4             int Class.classRedefinedCount      N/A
         92     4                 (loss due to the next object alignment)
    Instance size: 96 bytes
    Space losses: 36 bytes internal + 4 bytes external = 40 bytes total
    
    104

Class对象的大小为96, 和 104非常接近,如果运行的时候关闭压缩指针-XX:-UseCompressedOops,输出:

    Instance size: 152 bytes
    Space losses: 48 bytes internal + 4 bytes external = 52 bytes total

    160

也非常接近,而且两种情况第一个字段的偏移量和Class的长度都是相差8个字节,刚好是64位的一个字长。那么这中间的空档应该是instanceKlass的指针。

我们看到openjdk里面的javaClasses.cpp这里 是一个在线版本。

Klass* java_lang_Class::as_Klass(oop java_class) {
  //%note memory_2
  assert(java_lang_Class::is_instance(java_class), "must be a Class object");
  Klass* k = ((Klass*)java_class->metadata_field(_klass_offset));
  assert(k == NULL || k->is_klass(), "type check");
  return k;
}

这个方法是将Class对象转换成instanceKlass,可以看到instanceClass确实在Class对象的一定偏移量上(具体是多少不知道了)。那我认为他直接接在Class对象的尾部应该是合理的,而再后面就是类变量。(最起码jdk8是这样)

set put 还有对应的volatile,ordered版本。volatile对应volatile读写,很好理解。比如数组元素是没有办法声明为volatile的,你只能声明数组的引用是volatile,然后通过数组对象加偏移量调用这个方法来volatile读写数组元素 。而ordered版本对应Atomic包中的lazyset方法,参见注释(jdk src里面是没注释的,只能看openjdk里面的)。

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).

而看到Unsafe native的实现

// The non-intrinsified versions of setOrdered just use setVolatile

非常坑爹,这里面setOrdered和setVolatile是一模一样的,也就没办法知道他到底怎么搞的。

这两个东西和ConcurrentHashMap实现相关,但是我暂时记不起来了,空下来再补充。

    inline void OrderAccess::acquire() {
      volatile intptr_t local_dummy;
    #ifdef AMD64
      __asm__ volatile ("movq 0(%%rsp), %0" : "=r" (local_dummy) : : "memory");
    #else
      __asm__ volatile ("movl 0(%%esp),%0" : "=r" (local_dummy) : : "memory");
    #endif // AMD64
    }
    
    inline void OrderAccess::release() {
      // Avoid hitting the same cache-line from
      // different threads.
      volatile jint local_dummy = 0;
    }
    
    inline void OrderAccess::fence() {
      if (os::is_MP()) {
        // always use locked addl since mfence is sometimes expensive
    #ifdef AMD64
        __asm__ volatile ("lock; addl $0,0(%%rsp)" : : : "cc", "memory");
    #else
        __asm__ volatile ("lock; addl $0,0(%%esp)" : : : "cc", "memory");
    #endif
      }
    }

这里可以找到和四种内存屏障的对应:Memory Barriers

inline void OrderAccess::loadload()   { acquire(); }
inline void OrderAccess::storestore() { release(); }
inline void OrderAccess::loadstore()  { acquire(); }
inline void OrderAccess::storeload()  { fence(); }

前面的unsafe.putVolatile的实现也在这里面,

#define GET_FIELD_VOLATILE(obj, offset, type_name, v) \
      oop p = JNIHandles::resolve(obj); \
      if (support_IRIW_for_not_multiple_copy_atomic_cpu) { \
        OrderAccess::fence(); \
      } \
      volatile type_name v = OrderAccess::load_acquire((volatile type_name*)index_oop_from_field_offset_long(p, offset));
    
    #define SET_FIELD_VOLATILE(obj, offset, type_name, x) \
      oop p = JNIHandles::resolve(obj); \
      OrderAccess::release_store_fence((volatile type_name*)index_oop_from_field_offset_long(p, offset), truncate_##type_name(x));

volatile读:

    inline jint     OrderAccess::load_acquire(volatile jint*    p) { return *p; }

参数加上了volatile修饰符,直接返回。

volatile写:
  inline void     OrderAccess::release_store_fence(volatile jint*   p, jint   v) {
  __asm__ volatile (  "xchgl (%2),%0"
                    : "=r" (v)
                    : "0" (v), "r" (p)
                    : "memory");
}

查一下资料:

XCHG exchanges two operands. The operands can be in either order. If a memory operand is involved, BUS LOCK is asserted for the duration of the exchange, regardless of the presence or absence of the LOCK prefix or of the value of the IOPL.
总线锁和上面的LOCK指令相关
The LOCK# signal is asserted when there is a locked memory access due to uncacheable memory, locked operation that spans two cache lines, and page-walk from an uncacheable page table.

cas操作: cmpchg指令。

inline jint     Atomic::cmpxchg    (jint     exchange_value, volatile jint*     dest, jint     compare_value) {
  int mp = os::is_MP();
  __asm__ volatile (LOCK_IF_MP(%4) "cmpxchgl %1,(%3)"
                    : "=a" (exchange_value)
                    : "r" (exchange_value), "a" (compare_value), "r" (dest), "r" (mp)
                    : "cc", "memory");
  return exchange_value;
}
上一篇下一篇

猜你喜欢

热点阅读