本文基于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í)別的原子操作砖织,主要提供了以下功能:
- 通過(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();
- 可以定位對(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);
- 掛起與恢復(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");
}
}
- 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);