不安全的java类-Unsafe

作者: 图乐 | 来源:发表于2017-11-04 17:09 被阅读44次

    本文基于Android N源码分析

    前言
    Java最初被设计为一种安全的受控环境。尽管如此,HotSpot还是包含了一个后门sun.misc.Unsafe,提供了一些可以直接操控内存和线程的底层操作。Unsafe被JDK广泛应用于java.nio和并发包等实现中,这个不安全的类提供了一个观察HotSpot JVM内部结构并且可以对其进行修改,但是不建议在生产环境中使用。
    
    /**
     * A collection of methods for performing low-level, unsafe operations.
     * Although the class and all methods are public, use of this class is
     * limited because only trusted code can obtain instances of it.
     *
     * @author John R. Rose
     * @see #getUnsafe
     */
    

    执行低级、不安全操作的方法的集合,尽管类和所有方法都是公共的,但是这个类的使用是有限的,因为只有受信任的代码才能获取它的实例。这是在Android 源码中对这个类的注释。

    • Unsafe位于sun.misc包内,可以通过native方法直接操作堆外内存,可以随意查看及修改JVM中运行时的数据结构,例如查看和修改对象的成员,Unsafe的操作粒度不是类,而是数据和地址。
    • 如何获得Unsafe对象,Unsafe类里面可以看到有一个getUnsafe方法:
       /**
         * Gets the unique instance of this class. This is only allowed in
         * very limited situations.
         */
        public static Unsafe getUnsafe() {
            /*
             * Only code on the bootclasspath is allowed to get at the
             * Unsafe instance.
             */
            ClassLoader calling = VMStack.getCallingClassLoader();
            if ((calling != null) && (calling != Unsafe.class.getClassLoader())) {
                throw new SecurityException("Unsafe access denied");
            }
    
            return THE_ONE;
        }
    

    通过注释我们可以看出这个方法使用情况有限,只有在bootclasspath里面的代码才允许运行。如果我们想使用的话也不是没有办法那就是反射。
    在java环境

    public static Unsafe getUnsafe() {
       try {
               Field f = Unsafe.class.getDeclaredField("theUnsafe");
               f.setAccessible(true);
               return (Unsafe)f.get(null);
       } catch (Exception e) { 
           /* ... */
       }
    }
    

    android API下面无法直接获取到Unsafe这个类

        static {
            try {
                unsafeClass = Class.forName("sun.misc.Unsafe");
                if (Build.VERSION.SDK_INT >= 19) {
                    Field theUnsafeInstance = unsafeClass.getDeclaredField("theUnsafe");
                    theUnsafeInstance.setAccessible(true);
                    unsafe = theUnsafeInstance.get(null);
                } else {
                    Class AQSClass = Class.forName("java.util.concurrent.locks.AbstractQueuedSynchronizer");
                    Field theUnsafeInstance = AQSClass.getDeclaredField("unsafe");
                    theUnsafeInstance.setAccessible(true);
                    unsafe = theUnsafeInstance.get(null);
                }
    
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    

    要在Java层操作内容,也不是没有办法做到;JDK给我们留了一个后门:sun.misc.Unsafe 类;在OpenJDK里面这个类灰常强大,从内存操作到CAS到锁机制,但是在Android 平台还有一点点不一样,在 Android N之前,Android的JDK实现是 Apache Harmony,这个实现里面的Unsafe就有点鸡肋了,没法写内存;好在Android 又开了一个后门:Memory 类。


    java不能直接访问操作系统底层,而是通过本地方法来访问。Unsafe类提供了硬件级别的原子操作,主要提供了以下功能:

    1. 通过Unsafe类可以对内存进行操作;
      reallocateMemory方法并没有(N之前没有)
    public native long allocateMemory(long bytes);//分配内存
    public native void freeMemory(long address);//释放内存
    public native void copyMemory(long srcAddr, long dstAddr, long bytes);//复制内存
    public native int addressSize();
    public native int pageSize();
    
    1. 可以定位对象某字段的内存位置,也可以修改对象的字段值,即使它是私有的;
    /**
         * Gets the offset from the start of an array object's memory to
         * the memory used to store its initial (zeroeth) element.
         *
         * @param clazz non-null; class in question; must be an array class
         * @return the offset to the initial element
         */
        public int arrayBaseOffset(Class clazz) {}
    
        /**
         * Gets the size of each element of the given array class.
         *
         * @param clazz non-null; class in question; must be an array class
         * @return > 0; the size of each element of the array
         */
        public int arrayIndexScale(Class clazz) {}
    
        /**
         * Allocates an instance of the given class without running the constructor.
         * The class' <clinit> will be run, if necessary.
         */
        public native Object allocateInstance(Class<?> c);
    
    1. 挂起与恢复
      通过park方法挂起当前调用线程,通过unpark恢复一个线程(参数),线程操作相关还有一个LockSupport类的封装。
     /**
         * Parks the calling thread for the specified amount of time,
         * unless the "permit" for the thread is already available (due to
         * a previous call to {@link #unpark}. This method may also return
         * spuriously (that is, without the thread being told to unpark
         * and without the indicated amount of time elapsing).
         *
         * <p>See {@link java.util.concurrent.locks.LockSupport} for more
         * in-depth information of the behavior of this method.</p>
         *
         * @param absolute whether the given time value is absolute
         * milliseconds-since-the-epoch true or relative
         * nanoseconds-from-now false
         * @param time the (absolute millis or relative nanos) time value
         */
        public void park(boolean absolute, long time) {
            if (absolute) {
                Thread.currentThread().parkUntil$(time);
            } else {
                Thread.currentThread().parkFor$(time);
            }
        }
    
        /**
         * Unparks the given object, which must be a {@link Thread}.
         *
         * <p>See {@link java.util.concurrent.locks.LockSupport} for more
         * in-depth information of the behavior of this method.</p>
         *
         * @param obj non-null; the object to unpark
         */
        public void unpark(Object obj) {
            if (obj instanceof Thread) {
                ((Thread) obj).unpark$();
            } else {
                throw new IllegalArgumentException("valid for Threads only");
            }
        }
    
    1. CAS操作
      是通过compareAndSwapXXX方法实现的
     /**
         * Performs a compare-and-set operation on an int
         * field within the given object.
         *
         * @param obj non-null; object containing the field
         * @param offset offset to the field within obj
         * @param expectedValue expected value of the field
         * @param newValue new value to store in the field if the contents are
         * as expected
         * @return true if the new value was in fact stored, and
         * false if not
         */
        public native boolean compareAndSwapInt(Object obj, long offset,
                int expectedValue, int newValue);
    

    相关文章

      网友评论

        本文标题:不安全的java类-Unsafe

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