Java魔法类Unsafe详解
前言
Unsafe是jdk提供操作的增强执行低级别代码和操作底层资源的工具类。jdk1.8及以下的版本在sun.misc包下,jdk11移到jdk.internal.misc包下。sum.misc继续保留,只是实现修改为代理jdk.internal.misc包的实例。下面我们来详细分解以下Unsafe的功能和实现。
使用方法
看Unsafe的源码,我们发现Unsafe类是单例实现的,提供静态方法获取Unsafe实例。
public final class Unsafe {
private static final Unsafe theUnsafe;
private Unsafe() {
}
@CallerSensitive
public static Unsafe getUnsafe() {
Class var0 = Reflection.getCallerClass();
if (!VM.isSystemDomainLoader(var0.getClassLoader())) {
throw new SecurityException("Unsafe");
} else {
return theUnsafe;
}
}
}
我们在此处看到调用类必须是引导类加载器所加载的类才能调用。否则会抛出SecurityException异常。
即,如果我们的类是引导类加载器所加载的类那么我们就可以直接调用Unsafe的getUnsafe()获取Unsafe的实例。像jdk中java.util.concurrent包的很多并发、原子类一样直接使用:
private static final Unsafe unsafe = Unsafe.getUnsafe();
当时在我们实际开发中我们类,一般不是被引导类加载的,那么这时我们改如何使用呢?
- 第一种:通过-Xbootclasspath、-Xbootclasspath/a 、-Xbootclasspath/p把调用Unsafe的类所在的jar被引导类加载器加载,从而可以直接获得unsafe实例。
- 第二种:通过反射获取单例对象theUnsafe
Field field = Unsafe.class.getDeclaredField("theUnsafe");
field.setAccessible(true);
return (Unsafe) field.get(null);
推荐私用第二种方式。也是普遍的用户,如果还有其他方法,欢迎在文章后面留言探讨。
功能介绍
我们先来看一下Unsafe的代码,去除一些功能相似的代码,使用XXX代表不同的类型,简化阅读代码的难度。去除Deprecated标记的过期方法。本代码摘录自openjdk8u分支的jdk/src/share/classes/sun/misc/Unsafe.java。
通过整理分类,我们看到Unsafe主要提供了如下方法给我们使用:管理堆外内存、获取系统相关信息、(类、对象、字段、数组)的相关操作、CAS原子操作、线程相关操作、内存屏障。具体分类的详细方法在下面的源码整理中。
public final class Unsafe {
private static native void registerNatives();
static {
registerNatives();
sun.reflect.Reflection.registerMethodsToFilter(Unsafe.class, "getUnsafe");
}
private Unsafe() {}
private static final Unsafe theUnsafe = new Unsafe();
@CallerSensitive
public static Unsafe getUnsafe() {
Class<?> caller = Reflection.getCallerClass();
if (!VM.isSystemDomainLoader(caller.getClassLoader()))
throw new SecurityException("Unsafe");
return theUnsafe;
}
// 管理堆外内存
public native XXX getXXX(long address);
public native void putXXX(long address, XXX x);
public native long getAddress(long address);
public native void putAddress(long address, long x);
public native long allocateMemory(long bytes);
public native long reallocateMemory(long address, long bytes);
public native void setMemory(Object o, long offset, long bytes, byte value);
public void setMemory(long address, long bytes, byte value) {setMemory(null, address, bytes, value);}
public native void copyMemory(Object srcBase, long srcOffset, Object destBase, long destOffset, long bytes);
public void copyMemory(long srcAddress, long destAddress, long bytes) {copyMemory(null, srcAddress, null, destAddress, bytes);}
public native void freeMemory(long address);
// 系统相关信息
public native int addressSize();
public static final int ADDRESS_SIZE = theUnsafe.addressSize();
public native int pageSize();
public native int getLoadAverage(double[] loadavg, int nelems);
// 类、对象、数组的操作
public static final int INVALID_FIELD_OFFSET = -1;
public native long staticFieldOffset(Field f);
public native long objectFieldOffset(Field f);
public native Object staticFieldBase(Field f);
public native int arrayBaseOffset(Class<?> arrayClass);
public static final int ARRAY_XXX_BASE_OFFSET = theUnsafe.arrayBaseOffset(XXX[].class); // 一些base常量
public native int arrayIndexScale(Class<?> arrayClass);
public static final int ARRAY_XXX_INDEX_SCALE = theUnsafe.arrayIndexScale(XXX[].class); // 一些SCALE常量
public native boolean shouldBeInitialized(Class<?> c);
public native void ensureClassInitialized(Class<?> c);
public native Class<?> defineClass(String name, byte[] b, int off, int len, ClassLoader loader,ProtectionDomain protectionDomain);
public native Class<?> defineAnonymousClass(Class<?> hostClass, byte[] data, Object[] cpPatches);
public native Object allocateInstance(Class<?> cls) throws InstantiationException;
public native void throwException(Throwable ee);
public native int getXXX(Object o, long offset);
public native void putXXX(Object o, long offset, XXX x);
public native XXX getXXXVolatile(Object o, long offset);
public native void putXXXVolatile(Object o, long offset, XXX x);
public native void putOrderedXXX(Object o, long offset, XXX x);
// CAS原子操作
public final native boolean compareAndSwapXXX(Object o, long offset, XXX expected, XXX x);
public final XXX getAndAddXXX(Object o, long offset, XXX delta) {...}
public final XXX getAndSetXXX(Object o, long offset, XXX newValue) {...}
// 线程相关操作
public native void unpark(Object thread);
public native void park(boolean isAbsolute, long time);
// 内存屏障
public native void loadFence();
public native void storeFence();
public native void fullFence();
}
如果有对其中方法不理解的,可以看源文档中的注释,也可以看这篇文章,他通过对jdk中的实际应用来解释一些方法,是一篇难得的好文。
参考:
Java Magic. Part 4: sun.misc.Unsafe
源码分析
功能介绍中,我们通过源码整理和归类分析Unsafe.java提供的功能,我们看到了其中大部分的方法都是native方法,这不免勾起我们继续深究的兴趣,那我们就继续简单地看一下底层的实现。native方法是什么,如果不知道的朋友,请自行百度、google搜索。
我们以CAS指令为示例,我们看看底层是怎么实现Unsafe的native方法的。在此顺便简单说一下如果获取openjdk的源码。 通过hg clone http://hg.openjdk.java.net/jdk8u/jdk8u jdk8u
获取源码,再用clion打开就好。
我们在 hotspot/src/share/vm/prims
目录下找到了和unsafe相关的代码:
我们看一下compareAndSwapInt的实现:
我们看到调用的是Atomic的cmpxchg来完成最终的操作:
我们看到其中的核心方法为cmpxchg,该方法的具体实现,根据操作系统和cpu类型而不同
此处我们选择window_x86去简单看一下,其他的类似:
我们看到此处用的汇编指令来实现, 其中核心有两个:LOCK_IF_MP(mp)宏 和 cmpxchg汇编指令,具体的含义我们就不深究了,大致的意思是如果支持cmpxchg原子交换指令,则用原子交换指令,否则用锁实现。
上一篇: 多命名空间结合cgroup保障存储QoS