`

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可以很快的获取变量的值,但是在不了解的情况下不要随便调用。可能会不安全。
分享到:
评论

相关推荐

    并发编程总结.xmind

    java并发编程总结,包括多线程安全机制分析总结,Unsafe源码分析总结,并发工具类总结,ThreadLocal原理和使用,Fork/Join框架使用总结,同步容器和并发容器源码分析

    一篇看懂Java中的Unsafe类

    `Unsafe`类源码分析 `Unsafe`类包含大量本地方法,大致分为以下几个类别: 1. **Class相关**:提供对`Class`对象和静态字段的操作,如获取字段的内存偏移量。 2. **Object相关**:操作对象和其字段,如设置或...

    netty源码分析buffer

    ### Netty源码分析之Buffer #### Java Buffer 的相关基础知识 **1. Java 基本数据类型** Java 提供了八种基本数据类型:`byte`, `char`, `short`, `int`, `long`, `float`, `double`, `boolean`。 - **`byte`**:...

    免费开源-【Java学习+面试指南】部分内容大部分是Java程序员所需要掌握的核心知识

    Unsafe 详细解Java SPI 机制详解Java语法糖详解集合知识点/面试题总结:Java集合常见知识点&面试题总结(上)(必看)Java集合常见知识点&面试题总结(下)(必看)Java容器使用注意事项总结源码分析:ArrayList核心...

    golang源码分析–slice

    源码分析: 结构体定义 type slice struct { array unsafe.Pointer len int cap int } slice的结构体包含三个参数,指针,长度,容量 所以传递一个切片切片需要24字节的内存:指针字段需要8字节,长度和容量字段...

    【Java学习+面试指南】 一份涵盖大部分Java程序员所需要掌握的核心知识

    项目相关 项目介绍 使用建议 贡献指南 常见问题 Java 基础 知识点/面试题总结 : (必看 ): Java 基础常见知识点&面试题总结(上) Java 基础常见知识点&面试题总结(中) ...CopyOnWriteArrayList 核心源码分析

    java并发之AtomicInteger源码分析

    Java并发之AtomicInteger源码分析 AtomicInteger是Java并发包下面提供的原子类,主要操作的是int类型的整型,通过调用底层Unsafe的CAS等方法实现原子操作。下面是对AtomicInteger的源码分析。 1. 什么是原子操作...

    c# winform 把彩色图片转换为灰色的图片,变灰,灰度图片,速度很快,safe,unsafe

    在C# WinForm应用中,将彩色图像转换为灰度图像是一...对于实际应用,这种技术广泛应用于图像处理软件、监控系统以及需要实时图像分析的项目。了解并掌握这些技巧,可以帮助你构建更高效、更强大的C#图像处理应用程序。

    并发编程atomic&collections-课上笔记1

    本文主要讲述了 Java 中的并发编程,包括 atomic 包的介绍、CAS 算法的原理、ABA 问题的解决方案,以及 collections 中的 HashMap、HashTable 和 ConcurrentHashMap 的源码分析。 Atomic 包的介绍 ----------------...

    jdk 源码 src.zip

    通过对这些源码的分析,开发者不仅可以提升对Java语言的理解,还可以学习到如何设计高效、可扩展的系统。同时,对于想要进行JVM优化、字节码操作或者开发Java相关框架的程序员来说,深入JDK源码更是必不可少的步骤。...

    「Java学习+面试指南」一份涵盖大部分 Java 程序员所需要掌握的核心知识 准备 Java 面试,首选.zip

    源码分析 : ArrayList 源码+扩容机制分析 HashMap(JDK1.8)源码+底层数据结构分析 ConcurrentHashMap 源码+底层数据结构分析 IO IO 基础知识总结 IO 设计模式总结 IO 模型详解 并发 知识点/面试题总结 : (必看 ) ...

    「Java学习+面试指南」一份涵盖大部分 Java 程序员所需要掌握的核心知识

    源码分析: ArrayList 源码+扩容机制分析 HashMap(JDK1.8)源码+底层数据结构分析 ConcurrentHashMap 源码+底层数据结构分析 IO IO 基础知识总结 IO 设计模式总结 IO 模型详解 并发 知识点/面试题总结 : (必看 ) ...

    C#内存修改器最新源码

    在IT领域,内存修改器是一种工具,用于读取和修改应用程序的内存值,...通过学习和分析这样的源码,开发者可以深入了解操作系统级别的内存操作,提升对底层机制的理解,同时也为开发自己的内存相关工具打下坚实基础。

    JDK源码(sun包)

    - 提升问题排查能力,当遇到异常或性能问题时,源码分析能提供更深层次的见解。 5. **注意事项** 虽然研究sun包源码对开发者有诸多益处,但直接在生产环境中使用sun包的类可能会导致兼容性问题,因为这些类的API...

    JDK源码(含sun/com/sunw源码)

    1. **Java内存模型**:通过查看`sun.misc.Unsafe`和垃圾收集器源码,可以学习JVM如何管理内存和对象生命周期。 2. **类加载机制**:`sun.misc.Launcher`和`java.lang.ClassLoader`展示了类如何被加载到JVM中。 3. ...

    jdk 7 源码。含sun包下所有源码

    《深入解析JDK 7源码:聚焦sun包下的核心组件与nio》 JDK(Java Development Kit)是Java开发者的必备...通过阅读和分析源码,我们可以学习到许多设计模式、算法以及最佳实践,从而更好地应对实际开发中的各种挑战。

    jdk1.8源码包含sun

    在研究JDK1.8源码时,可以将"share\classes"和"windows\classes"目录下的源代码引入IDE或Eclipse,方便查阅和分析。 总之,"sun"包是JDK1.8源码中的一个重要组成部分,它揭示了Java平台的许多核心秘密。深入研究...

    java 1.6 完整源码(包括 sun)

    Java 1.6,也被称为Java SE 6,...总结,Java 1.6的源码分析可以帮助我们深入了解Java平台的工作原理,学习和研究各种内部机制,同时"sun"包的探索则能让我们洞察Oracle JDK的实现细节,但需谨慎使用其中的非公开API。

    jdk sun 开头的源码

    学习和分析这些"sun"开头的源码,可以帮助开发者深入理解Java的底层工作原理,提升编程技能,解决一些特定场景下的问题。不过需要注意的是,由于这些源码属于Oracle私有的实现,其接口和行为可能在未来版本中改变,...

    java 中Buffer源码的分析

    Java 中 Buffer 源码的分析 Java 中 Buffer 源码的分析是 Java 中一个非常重要的知识点, Buffer 是 Java 中用于高效进行 IO 操作的缓冲区。Buffer 分为两类:Direct Buffer 和 Heap Buffer。下面将详细介绍这两类 ...

Global site tag (gtag.js) - Google Analytics