美文网首页
Jdk之Unsafe总结

Jdk之Unsafe总结

作者: 知止9528 | 来源:发表于2019-01-26 14:14 被阅读84次

    因为Netty里面用到了Unsafe对象,所以就回过头来看了一下

    前言

    Java 不能直接访问操作系统底层,而是通过本地方法来访问。Unsafe 类提供了硬件级别的原子操作。
    Unsafe 类在 sun.misc 包下,不属于 Java 标准。很多 Java 的基础类库,包括一些被广泛使用的高性能开发库都是基于 Unsafe 类开发,比如 Netty 等。
    Unsafe 是用于在实质上扩展 Java 语言表达能力、便于在更高层(Java 层)代码里实现原本要在更低层(C 层)实现的核心库功能用的。
    这些功能包括裸内存的申请/释放/访问,低层硬件的 atomic/volatile 支持,创建未初始化对象等。
    它原本的设计就只应该被标准库使用,因此不建议在生产环境中使用。


    Unsafe 对象不能直接通过 new Unsafe() 或调用 Unsafe.getUnsafe() 获取。
    Unsafe 被设计成单例模式,构造方法私有。
    getUnsafe 被设计成只能从引导类加载器(bootstrap class loader)加载。

    那么我们怎么来获取呢?

    Field f = Unsafe.class.getDeclaredField("theUnsafe");
    f.setAccessible(true);
    Unsafe unsafe = (Unsafe) f.get(null);
    

    常用方法

    Class 相关

    主要提供 Class 和它的静态字段的操作方法。

    /静态属性的偏移量,用于在对应的 Class 对象中读写静态属性
    public native long staticFieldOffset(Field f);
      
    public native Object staticFieldBase(Field f);
    //判断是否需要初始化一个类
    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 defineClass(String var1, byte[] var2, int var3, int var4);
    //定义一个匿名类,可用于动态创建类
    public native Class defineAnonymousClass(Class hostClass, byte[] data, Object[] cpPatches);
    

    Object 相关

    Java 中的基本类型(boolean、byte、char、short、int、long、float、double)及对象引用类型都有以下方法。

    //获得对象的字段偏移量 
    public native long objectFieldOffset(Field f); 
    //获得给定对象地址偏移量的int值
    public native int getInt(Object o, long offset);
    //设置给定对象地址偏移量的int值
    public native void putInt(Object o, long offset, int x);
    //获得给定对象地址偏移量的值
    public native Object getObject(Object o, long offset);
    //设置给定对象地址偏移量的值
    public native void putObject(Object o, long offset, Object x);
    //创建对象,但并不会调用其构造方法。如果类未被初始化,将初始化类。
    public native Object allocateInstance(Class cls) throws InstantiationException;
    

    数组相关

    通过 arrayBaseOffset 和 arrayIndexScale 可定位数组中每个元素在内存中的位置。

    //返回数组中第一个元素的偏移地址
    public native int arrayBaseOffset(Class arrayClass);
    //boolean、byte、short、char、int、long、float、double,及对象类型均有以下方法
    /** The value of {@code arrayBaseOffset(boolean[].class)} */
    public static final int ARRAY_BOOLEAN_BASE_OFFSET = theUnsafe.arrayBaseOffset(boolean[].class);
      
    /**
     * Report the scale factor for addressing elements in the storage
     * allocation of a given array class. However, arrays of "narrow" types
     * will generally not work properly with accessors like {@link
     * #getByte(Object, int)}, so the scale factor for such classes is reported
     * as zero.
     *
     * @see #arrayBaseOffset
     * @see #getInt(Object, long)
     * @see #putInt(Object, long, int)
     */
    //返回数组中每一个元素占用的大小
    public native int arrayIndexScale(Class arrayClass);
      
    //boolean、byte、short、char、int、long、float、double,及对象类型均有以下方法
    /** The value of {@code arrayIndexScale(boolean[].class)} */
    public static final int ARRAY_BOOLEAN_INDEX_SCALE = theUnsafe.arrayIndexScale(boolean[].class);
    

    CAS 相关

    compareAndSwap,内存偏移地址 offset,预期值 expected,新值 x。如果变量在当前时刻的值和预期值 expected 相等,尝试将变量的值更新为 x。如果更新成功,返回 true;否则,返回 false。

    //更新变量值为x,如果当前值为expected
    //o:对象 offset:偏移量 expected:期望值 x:新值
    public final native boolean compareAndSwapObject(Object o, long offset, Object expected, Object x);
      
    public final native boolean compareAndSwapInt(Object o, long offset, int expected, int x);
      
    public final native boolean compareAndSwapLong(Object o, long offset, long expected, long x);
    

    线程调度相关

    主要包括监视器锁定、解锁等。

    //取消阻塞线程
    public native void unpark(Object thread);
    //阻塞线程
    public native void park(boolean isAbsolute, long time);
    //获得对象锁
    public native void monitorEnter(Object o);
    //释放对象锁
    public native void monitorExit(Object o);
    //尝试获取对象锁,返回 true 或 false 表示是否获取成功
    public native boolean tryMonitorEnter(Object o);
    

    volatile 相关读写

    //从对象的指定偏移量处获取变量的引用,使用 volatile 的加载语义
    //相当于 getObject(Object, long) 的 volatile 版本
    //从主存中获取值
    public native Object getObjectVolatile(Object o, long offset);
      
    //存储变量的引用到对象的指定的偏移量处,使用 volatile 的存储语义
    //相当于 putObject(Object, long, Object) 的 volatile 版本
    //设置值刷新主存
    public native void putObjectVolatile(Object o, long offset, Object x);
    /**
     * Version of {@link #putObjectVolatile(Object, long, Object)}
     * that does not guarantee immediate visibility of the store to
     * other threads. This method is generally only useful if the
     * underlying field is a Java volatile (or if an array cell, one
     * that is otherwise only accessed using volatile accesses).
     */
    public native void putOrderedObject(Object o, long offset, Object x);
      
    /** Ordered/Lazy version of {@link #putIntVolatile(Object, long, int)} */
    public native void putOrderedInt(Object o, long offset, int x);
      
    /** Ordered/Lazy version of {@link #putLongVolatile(Object, long, long)} */
    public native void putOrderedLong(Object o, long offset, long x);
    

    内存屏障相关

    JDK 1.8 引入 ,用于定义内存屏障,避免代码重排序。

    //内存屏障,禁止 load 操作重排序,即屏障前的load操作不能被重排序到屏障后,屏障后的 load 操作不能被重排序到屏障前
    public native void loadFence();
    //内存屏障,禁止 store 操作重排序,即屏障前的 store 操作不能被重排序到屏障后,屏障后的 store 操作不能被重排序到屏障前
    public native void storeFence();
    //内存屏障,禁止 load、store 操作重排序
    public native void fullFence();
    

    内存管理(非堆内存)

    allocateMemory 所分配的内存需要手动 free(不被 GC 回收)

    //(boolean、byte、char、short、int、long、float、double) 都有以下 get、put 两个方法。 
    //获得给定地址上的 int 值
    public native int getInt(long address);
    //设置给定地址上的 int 值
    public native void putInt(long address, int 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);
    

    系统相关

    //返回指针的大小。返回值为 4 或 8。
    public native int addressSize();
    
    /** The value of {@code addressSize()} */
    public static final int ADDRESS_SIZE = theUnsafe.addressSize();
      
    //内存页的大小。
    public native int pageSize();
    

    其它

    //获取系统的平均负载值,loadavg 这个 double 数组将会存放负载值的结果,nelems 决定样本数量,nelems 只能取值为 1 到 3,分别代表最近 1、5、15 分钟内系统的平均负载。
    //如果无法获取系统的负载,此方法返回 -1,否则返回获取到的样本数量(loadavg 中有效的元素个数)。
    public native int getLoadAverage(double[] loadavg, int nelems);
    //绕过检测机制直接抛出异常。
    public native void throwException(Throwable ee);
    

    相关文章

      网友评论

          本文标题:Jdk之Unsafe总结

          本文链接:https://www.haomeiwen.com/subject/pidijqtx.html