这几天在分析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的源码链接[url]http://www.docjar.com/html/api/sun/misc/Unsafe.java.html[/url]
总结:虽然unsafe可以很快的获取变量的值,但是在不了解的情况下不要随便调用。可能会不安全。