不安全的java類-Unsafe

本文基于Android N源碼分析

前言
Java最初被設(shè)計(jì)為一種安全的受控環(huán)境泳秀。盡管如此,HotSpot還是包含了一個(gè)后門sun.misc.Unsafe朱沃,提供了一些可以直接操控內(nèi)存和線程的底層操作苞轿。Unsafe被JDK廣泛應(yīng)用于java.nio和并發(fā)包等實(shí)現(xiàn)中,這個(gè)不安全的類提供了一個(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í)、不安全操作的方法的集合翎卓,盡管類和所有方法都是公共的契邀,但是這個(gè)類的使用是有限的,因?yàn)橹挥惺苄湃蔚拇a才能獲取它的實(shí)例失暴。這是在Android 源碼中對(duì)這個(gè)類的注釋坯门。

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

    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è)后門:sun.misc.Unsafe 類;在OpenJDK里面這個(gè)類灰常強(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)法寫內(nèi)存稳捆;好在Android 又開了一個(gè)后門:Memory 類赠法。


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

  1. 通過(guò)Unsafe類可以對(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類的封裝眶熬。
 /**
     * 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)系作者
  • 序言:七十年代末妹笆,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子娜氏,更是在濱河造成了極大的恐慌拳缠,老刑警劉巖,帶你破解...
    沈念sama閱讀 207,113評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件牍白,死亡現(xiàn)場(chǎng)離奇詭異脊凰,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)茂腥,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評(píng)論 2 381
  • 文/潘曉璐 我一進(jìn)店門狸涌,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人最岗,你說(shuō)我怎么就攤上這事帕胆。” “怎么了般渡?”我有些...
    開封第一講書人閱讀 153,340評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵懒豹,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我驯用,道長(zhǎng)脸秽,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,449評(píng)論 1 279
  • 正文 為了忘掉前任蝴乔,我火速辦了婚禮记餐,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘薇正。我一直安慰自己片酝,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,445評(píng)論 5 374
  • 文/花漫 我一把揭開白布挖腰。 她就那樣靜靜地躺著雕沿,像睡著了一般。 火紅的嫁衣襯著肌膚如雪猴仑。 梳的紋絲不亂的頭發(fā)上审轮,一...
    開封第一講書人閱讀 49,166評(píng)論 1 284
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼断国。 笑死贤姆,一個(gè)胖子當(dāng)著我的面吹牛榆苞,可吹牛的內(nèi)容都是我干的稳衬。 我是一名探鬼主播,決...
    沈念sama閱讀 38,442評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼坐漏,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼薄疚!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起赊琳,我...
    開封第一講書人閱讀 37,105評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤街夭,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后躏筏,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體板丽,經(jīng)...
    沈念sama閱讀 43,601評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,066評(píng)論 2 325
  • 正文 我和宋清朗相戀三年趁尼,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了埃碱。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,161評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡酥泞,死狀恐怖砚殿,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情芝囤,我是刑警寧澤似炎,帶...
    沈念sama閱讀 33,792評(píng)論 4 323
  • 正文 年R本政府宣布,位于F島的核電站悯姊,受9級(jí)特大地震影響羡藐,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜悯许,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,351評(píng)論 3 307
  • 文/蒙蒙 一仆嗦、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧岸晦,春花似錦欧啤、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至冈在,卻和暖如春倒慧,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評(píng)論 1 261
  • 我被黑心中介騙來(lái)泰國(guó)打工纫谅, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留炫贤,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,618評(píng)論 2 355
  • 正文 我出身青樓付秕,卻偏偏與公主長(zhǎng)得像兰珍,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子询吴,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,916評(píng)論 2 344

推薦閱讀更多精彩內(nèi)容