不安全的java類(lèi)-Unsafe

本文基于Android N源碼分析

前言
Java最初被設(shè)計(jì)為一種安全的受控環(huán)境。盡管如此,HotSpot還是包含了一個(gè)后門(mén)sun.misc.Unsafe,提供了一些可以直接操控內(nèi)存和線程的底層操作。Unsafe被JDK廣泛應(yīng)用于java.nio和并發(fā)包等實(shí)現(xiàn)中,這個(gè)不安全的類(lèi)提供了一個(gè)觀察HotSpot JVM內(nèi)部結(jié)構(gòu)并且可以對(duì)其進(jìn)行修改,但是不建議在生產(chǎn)環(huán)境中使用。
/**
 * 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
 */

執(zhí)行低級(jí)、不安全操作的方法的集合,盡管類(lèi)和所有方法都是公共的,但是這個(gè)類(lèi)的使用是有限的,因?yàn)橹挥惺苄湃蔚拇a才能獲取它的實(shí)例。這是在Android 源碼中對(duì)這個(gè)類(lèi)的注釋。

  • Unsafe位于sun.misc包內(nèi),可以通過(guò)native方法直接操作堆外內(nèi)存,可以隨意查看及修改JVM中運(yùn)行時(shí)的數(shù)據(jù)結(jié)構(gòu),例如查看和修改對(duì)象的成員,Unsafe的操作粒度不是類(lèi),而是數(shù)據(jù)和地址。
  • 如何獲得Unsafe對(duì)象,Unsafe類(lèi)里面可以看到有一個(gè)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;
    }

通過(guò)注釋我們可以看出這個(gè)方法使用情況有限,只有在bootclasspath里面的代碼才允許運(yùn)行。如果我們想使用的話也不是沒(méi)有辦法那就是反射。
在java環(huán)境

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

android API下面無(wú)法直接獲取到Unsafe這個(gè)類(lèi)

    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層操作內(nèi)容,也不是沒(méi)有辦法做到;JDK給我們留了一個(gè)后門(mén):sun.misc.Unsafe 類(lèi);在OpenJDK里面這個(gè)類(lèi)灰常強(qiáng)大,從內(nèi)存操作到CAS到鎖機(jī)制,但是在Android 平臺(tái)還有一點(diǎn)點(diǎn)不一樣,在 Android N之前,Android的JDK實(shí)現(xiàn)是 Apache Harmony,這個(gè)實(shí)現(xiàn)里面的Unsafe就有點(diǎn)雞肋了,沒(méi)法寫(xiě)內(nèi)存;好在Android 又開(kāi)了一個(gè)后門(mén):Memory 類(lèi)。


java不能直接訪問(wèn)操作系統(tǒng)底層,而是通過(guò)本地方法來(lái)訪問(wèn)。Unsafe類(lèi)提供了硬件級(jí)別的原子操作,主要提供了以下功能:

  1. 通過(guò)Unsafe類(lèi)可以對(duì)內(nèi)存進(jìn)行操作;
    reallocateMemory方法并沒(méi)有(N之前沒(méi)有)
public native long allocateMemory(long bytes);//分配內(nèi)存
public native void freeMemory(long address);//釋放內(nèi)存
public native void copyMemory(long srcAddr, long dstAddr, long bytes);//復(fù)制內(nèi)存
public native int addressSize();
public native int pageSize();
  1. 可以定位對(duì)象某字段的內(nèi)存位置,也可以修改對(duì)象的字段值,即使它是私有的;
/**
     * 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. 掛起與恢復(fù)
    通過(guò)park方法掛起當(dāng)前調(diào)用線程,通過(guò)unpark恢復(fù)一個(gè)線程(參數(shù)),線程操作相關(guān)還有一個(gè)LockSupport類(lèi)的封裝。
 /**
     * 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操作
    是通過(guò)compareAndSwapXXX方法實(shí)現(xiàn)的
 /**
     * 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);
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容