这几天在分析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可以很快的获取变量的值,但是在不了解的情况下不要随便调用。可能会不安全。
分享到:
相关推荐
java并发编程总结,包括多线程安全机制分析总结,Unsafe源码分析总结,并发工具类总结,ThreadLocal原理和使用,Fork/Join框架使用总结,同步容器和并发容器源码分析
`Unsafe`类源码分析 `Unsafe`类包含大量本地方法,大致分为以下几个类别: 1. **Class相关**:提供对`Class`对象和静态字段的操作,如获取字段的内存偏移量。 2. **Object相关**:操作对象和其字段,如设置或...
### Netty源码分析之Buffer #### Java Buffer 的相关基础知识 **1. Java 基本数据类型** Java 提供了八种基本数据类型:`byte`, `char`, `short`, `int`, `long`, `float`, `double`, `boolean`。 - **`byte`**:...
Unsafe 详细解Java SPI 机制详解Java语法糖详解集合知识点/面试题总结:Java集合常见知识点&面试题总结(上)(必看)Java集合常见知识点&面试题总结(下)(必看)Java容器使用注意事项总结源码分析:ArrayList核心...
源码分析: 结构体定义 type slice struct { array unsafe.Pointer len int cap int } slice的结构体包含三个参数,指针,长度,容量 所以传递一个切片切片需要24字节的内存:指针字段需要8字节,长度和容量字段...
项目相关 项目介绍 使用建议 贡献指南 常见问题 Java 基础 知识点/面试题总结 : (必看 ): Java 基础常见知识点&面试题总结(上) Java 基础常见知识点&面试题总结(中) ...CopyOnWriteArrayList 核心源码分析
Java并发之AtomicInteger源码分析 AtomicInteger是Java并发包下面提供的原子类,主要操作的是int类型的整型,通过调用底层Unsafe的CAS等方法实现原子操作。下面是对AtomicInteger的源码分析。 1. 什么是原子操作...
在C# WinForm应用中,将彩色图像转换为灰度图像是一...对于实际应用,这种技术广泛应用于图像处理软件、监控系统以及需要实时图像分析的项目。了解并掌握这些技巧,可以帮助你构建更高效、更强大的C#图像处理应用程序。
本文主要讲述了 Java 中的并发编程,包括 atomic 包的介绍、CAS 算法的原理、ABA 问题的解决方案,以及 collections 中的 HashMap、HashTable 和 ConcurrentHashMap 的源码分析。 Atomic 包的介绍 ----------------...
通过对这些源码的分析,开发者不仅可以提升对Java语言的理解,还可以学习到如何设计高效、可扩展的系统。同时,对于想要进行JVM优化、字节码操作或者开发Java相关框架的程序员来说,深入JDK源码更是必不可少的步骤。...
源码分析 : ArrayList 源码+扩容机制分析 HashMap(JDK1.8)源码+底层数据结构分析 ConcurrentHashMap 源码+底层数据结构分析 IO IO 基础知识总结 IO 设计模式总结 IO 模型详解 并发 知识点/面试题总结 : (必看 ) ...
源码分析: ArrayList 源码+扩容机制分析 HashMap(JDK1.8)源码+底层数据结构分析 ConcurrentHashMap 源码+底层数据结构分析 IO IO 基础知识总结 IO 设计模式总结 IO 模型详解 并发 知识点/面试题总结 : (必看 ) ...
在IT领域,内存修改器是一种工具,用于读取和修改应用程序的内存值,...通过学习和分析这样的源码,开发者可以深入了解操作系统级别的内存操作,提升对底层机制的理解,同时也为开发自己的内存相关工具打下坚实基础。
- 提升问题排查能力,当遇到异常或性能问题时,源码分析能提供更深层次的见解。 5. **注意事项** 虽然研究sun包源码对开发者有诸多益处,但直接在生产环境中使用sun包的类可能会导致兼容性问题,因为这些类的API...
1. **Java内存模型**:通过查看`sun.misc.Unsafe`和垃圾收集器源码,可以学习JVM如何管理内存和对象生命周期。 2. **类加载机制**:`sun.misc.Launcher`和`java.lang.ClassLoader`展示了类如何被加载到JVM中。 3. ...
《深入解析JDK 7源码:聚焦sun包下的核心组件与nio》 JDK(Java Development Kit)是Java开发者的必备...通过阅读和分析源码,我们可以学习到许多设计模式、算法以及最佳实践,从而更好地应对实际开发中的各种挑战。
在研究JDK1.8源码时,可以将"share\classes"和"windows\classes"目录下的源代码引入IDE或Eclipse,方便查阅和分析。 总之,"sun"包是JDK1.8源码中的一个重要组成部分,它揭示了Java平台的许多核心秘密。深入研究...
Java 1.6,也被称为Java SE 6,...总结,Java 1.6的源码分析可以帮助我们深入了解Java平台的工作原理,学习和研究各种内部机制,同时"sun"包的探索则能让我们洞察Oracle JDK的实现细节,但需谨慎使用其中的非公开API。
学习和分析这些"sun"开头的源码,可以帮助开发者深入理解Java的底层工作原理,提升编程技能,解决一些特定场景下的问题。不过需要注意的是,由于这些源码属于Oracle私有的实现,其接口和行为可能在未来版本中改变,...
Java 中 Buffer 源码的分析 Java 中 Buffer 源码的分析是 Java 中一个非常重要的知识点, Buffer 是 Java 中用于高效进行 IO 操作的缓冲区。Buffer 分为两类:Direct Buffer 和 Heap Buffer。下面将详细介绍这两类 ...