Unsafe 源码分析

这几天在分析ThreadPoolExecutor的时候看到了Unsafe类就研究了一下。
Unsafe的源代码可以见http://xiao-jiang51.iteye.com/blog/850413
的分析。
因为Unsafe定义了私有的构造函数,而且限制类用户定义的类来访问Unsafe对象。如下代码,如果是开发人员自定义的类去调用的话,就会返回SecurityException.这个类怎么控制非java自带类不能访问unsafe类呢,先调用getCallerClass方法获得那个类调用unsafe,然后获得那个类的classloader,如果classloader是空就是引导加载器加载的类,也就是java自带类,这样我们可以写类似的代码,然后判断是不是我们自己写的类加载的,如果不是就不能访问当前的类。http://blog.csdn.net/lovingprince/article/details/4238695
 public static Unsafe getUnsafe()  
   {  
   Class localClass = Reflection.getCallerClass(2);  
   if (localClass.getClassLoader() != null)  
   throw new SecurityException("Unsafe");  
   return theUnsafe;  
   } 

在http://xiao-jiang51.iteye.com/blog/850413这篇文章中通过来获取unsafe对象。
field = Unsafe.class.getDeclaredField("theUnsafe");   
      field.setAccessible(true);   
      // 获取静态属性,Unsafe在启动JVM时随rt.jar装载   
      unsafe = (Unsafe) field.get(null);   

通过千辛万苦拿到unsafe对象,那么到底有什么用呢?我记得面试的时候有面试官问过说怎么不通过反射来设置值,现在终于知道了,原来可以通过unsafe来设置,而且unsafe的效率比反射高出很多。
package com.fnk.unsafetest;

 import java.io.Serializable;  
 import java.lang.reflect.Field;  
 import sun.misc.Unsafe;  
 /** 
  * @author haitao.yao Dec 14, 2010 
  */  
 public class ReflectionCompare {  
   private static final int count = 10000000;  
   /** 
    * @param args 
    */  
   public static void main(String[] args) {  
     long duration = testIntCommon();  
     System.out.println("int common test for  " + count  
         + " times, duration: " + duration);  
     duration = testUnsafe();  
     System.out.println("int unsafe test for  " + count  
         + " times, duration: " + duration);  
   }  
   private static long testUnsafe() {  
     long start = System.currentTimeMillis();  
     sun.misc.Unsafe unsafe = getUnsafe();  
     int temp = count;  
     Field field = getIntField();  
   
     TestBean tb =  new TestBean();
     tb.setAge(50);
     int age;
     long offset = unsafe.objectFieldOffset(field);  
     while (temp-- > 0) {  
       age = unsafe.getInt(tb, offset);  
      
     }  
     return System.currentTimeMillis() - start;  
   }  
   private static long testIntCommon() {  
    
     int temp = count;  
     getIntField().setAccessible(true);  
     TestBean bean = new TestBean();  
     bean.setAge(50);
     int age;
     long start = System.currentTimeMillis();  
     try {  
       while (temp-- > 0) {  
         age = (Integer)getIntField().get(bean);
       
       }  
       return System.currentTimeMillis() - start;  
     } catch (Exception e) {  
       e.printStackTrace();  
     }  
     return -1;  
   }  
   private static final sun.misc.Unsafe unsafe;  
   static {  
     sun.misc.Unsafe value = null;  
     try {  
       Class<?> clazz = Class.forName("sun.misc.Unsafe");  
       Field field = clazz.getDeclaredField("theUnsafe");  
       field.setAccessible(true);  
       value = (Unsafe) field.get(null);  
     } catch (Exception e) {  
       e.printStackTrace();  
       throw new RuntimeException("error to get theUnsafe", e);  
     }  
     unsafe = value;  
   }  
   public static final sun.misc.Unsafe getUnsafe() {  
     return unsafe;  
   }  
   private static final Field intField;  
   private static final Field stringField;  
   static {  
     try {  
       intField = TestBean.class.getDeclaredField("age");  
       stringField = TestBean.class.getDeclaredField("name");  
     } catch (Exception e) {  
       e.printStackTrace();  
       throw new IllegalStateException("failed to init testbean field", e);  
     }  
   }  
   public static final Field getIntField() {  
     return intField;  
   }  
   public static final Field getStringField() {  
     return stringField;  
   }  
     
   /** 
    * @author haitao.yao 
    * Dec 14, 2010 
    */  
   static class TestBean implements Serializable{  
     /** 
      *  
      */  
     private static final long serialVersionUID = -5994966479456252766L;  
       
     private String name;  
     private int age;  
     /** 
      * @return the name 
      */  
     public String getName() {  
       return name;  
     }  
     /** 
      * @param name the name to set 
      */  
     public void setName(String name) {  
       this.name = name;  
     }  
     /** 
      * @return the age 
      */  
     public int getAge() {  
       return age;  
     }  
     /** 
      * @param age the age to set 
      */  
     public void setAge(int age) {  
       this.age = age;  
     }  
   }  


int common test for  10000000 times, duration: 1672
int unsafe test for  10000000 times, duration: 47

那么Field.get到底是怎么实现的呢?接下来我们来分析源代码。
Class Field{
//get 方法,通过调用getFieldAccessor的get方法
 public Object get(Object obj)
        throws IllegalArgumentException, IllegalAccessException
    {
        return getFieldAccessor(obj).get(obj);
    }
//返回overrideFieldAccessor或者fieldAccessor或者当overrideFieldAccessor 和fieldAccessor都为空时返回acquireFieldAccessor()方法的返回值
 private FieldAccessor getFieldAccessor(Object obj)
        throws IllegalAccessException
    {
        doSecurityCheck(obj);
        boolean ov = override;
        FieldAccessor a = (ov)? overrideFieldAccessor : fieldAccessor;
        return (a != null)? a : acquireFieldAccessor(ov);
    }
}

//返回root.getFieldAccessor()。root也是一个Filed对象。root是为了当调用copy函数的时候可以共享fieldAccessor
private FieldAccessor acquireFieldAccessor(boolean overrideFinalCheck) {
        // First check to see if one has been created yet, and take it
        // if so
        FieldAccessor tmp = null;
        if (root != null) tmp = root.getFieldAccessor(overrideFinalCheck);
        if (tmp != null) {
            if (overrideFinalCheck)
                overrideFieldAccessor = tmp;
            else
                fieldAccessor = tmp;
        } else {
            // Otherwise fabricate one and propagate it up to the root
            tmp = reflectionFactory.newFieldAccessor(this, overrideFinalCheck);
            setFieldAccessor(tmp, overrideFinalCheck);
        }
        return tmp;
    }

Field copy() {
        // This routine enables sharing of FieldAccessor objects
        // among Field objects which refer to the same underlying
        // method in the VM. (All of this contortion is only necessary
        // because of the "accessibility" bit in AccessibleObject,
        // which implicitly requires that new java.lang.reflect
        // objects be fabricated for each reflective call on Class
        // objects.)
        Field res = new Field(clazz, name, type, modifiers, slot, signature, annotations);
        res.root = this;
        // Might as well eagerly propagate this if already present
        res.fieldAccessor = fieldAccessor;
        res.overrideFieldAccessor = overrideFieldAccessor;
        return res;
    }

可以看到在UnsafeObjectFieldAccessorImpl 中是调用unsafe.getObject()函数来实现的。那么为什么性能上会差那么多呢?引用http://rednaxelafx.iteye.com/blog/1102979的分析,两边最终干活的都是Unsafe.getObject(),Unsafe.getObject()但直接调就优化掉了,而通过Field.get()来调就没被优化掉。
Class ReflectionFactory{
public FieldAccessor newFieldAccessor(Field field, boolean override) {

      checkInitted();

      return UnsafeFieldAccessorFactory.newFieldAccessor(field, override);

      }
}

class UnsafeObjectFieldAccessorImpl extends UnsafeFieldAccessorImpl {
    UnsafeObjectFieldAccessorImpl(Field field) {
        super(field);
    }

    public Object get(Object obj) throws IllegalArgumentException {
        ensureObj(obj);
        return unsafe.getObject(obj, fieldOffset);
    }
}


unsafe的源码链接 http://www.docjar.com/html/api/sun/misc/Unsafe.java.html

总结:虽然unsafe可以很快的获取变量的值,但是在不了解的情况下不要随便调用。可能会不安全。

你可能感兴趣的:(Unsafe 源码分析)