欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Java魔法类Unsafe详解

程序员文章站 2024-03-14 21:17:17
...

前言

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();

当时在我们实际开发中我们类,一般不是被引导类加载的,那么这时我们改如何使用呢?

  1. 第一种:通过-Xbootclasspath、-Xbootclasspath/a 、-Xbootclasspath/p把调用Unsafe的类所在的jar被引导类加载器加载,从而可以直接获得unsafe实例。
  2. 第二种:通过反射获取单例对象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

JAVA中神奇的双刃剑–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相关的代码:
Java魔法类Unsafe详解我们看一下compareAndSwapInt的实现:
Java魔法类Unsafe详解我们看到调用的是Atomic的cmpxchg来完成最终的操作:
Java魔法类Unsafe详解我们看到其中的核心方法为cmpxchg,该方法的具体实现,根据操作系统和cpu类型而不同
Java魔法类Unsafe详解此处我们选择window_x86去简单看一下,其他的类似:
Java魔法类Unsafe详解我们看到此处用的汇编指令来实现, 其中核心有两个:LOCK_IF_MP(mp)宏 和 cmpxchg汇编指令,具体的含义我们就不深究了,大致的意思是如果支持cmpxchg原子交换指令,则用原子交换指令,否则用锁实现。

相关标签: Java