本文基于Android N源碼分析
前言
Java最初被設(shè)計(jì)為一種安全的受控環(huán)境。盡管如此,HotSpot還是包含了一個后門sun.misc.Unsafe,提供了一些可以直接操控內(nèi)存和線程的底層操作。Unsafe被JDK廣泛應(yīng)用于java.nio和并發(fā)包等實(shí)現(xiàn)中,這個不安全的類提供了一個觀察HotSpot JVM內(nèi)部結(jié)構(gòu)并且可以對其進(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í)行低級、不安全操作的方法的集合,盡管類和所有方法都是公共的,但是這個類的使用是有限的,因?yàn)橹挥惺苄湃蔚拇a才能獲取它的實(shí)例。這是在Android 源碼中對這個類的注釋。
- Unsafe位于sun.misc包內(nèi),可以通過native方法直接操作堆外內(nèi)存,可以隨意查看及修改JVM中運(yùn)行時的數(shù)據(jù)結(jié)構(gòu),例如查看和修改對象的成員,Unsafe的操作粒度不是類,而是數(shù)據(jù)和地址。
- 如何獲得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里面的代碼才允許運(yùn)行。如果我們想使用的話也不是沒有辦法那就是反射。
在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下面無法直接獲取到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層操作內(nèi)容,也不是沒有辦法做到;JDK給我們留了一個后門:sun.misc.Unsafe 類;在OpenJDK里面這個類灰常強(qiáng)大,從內(nèi)存操作到CAS到鎖機(jī)制,但是在Android 平臺還有一點(diǎn)點(diǎn)不一樣,在 Android N之前,Android的JDK實(shí)現(xiàn)是 Apache Harmony,這個實(shí)現(xiàn)里面的Unsafe就有點(diǎn)雞肋了,沒法寫內(nèi)存;好在Android 又開了一個后門:Memory 類。
java不能直接訪問操作系統(tǒng)底層,而是通過本地方法來訪問。Unsafe類提供了硬件級別的原子操作,主要提供了以下功能:
- 通過Unsafe類可以對內(nèi)存進(jìn)行操作;
reallocateMemory方法并沒有(N之前沒有)
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();
- 可以定位對象某字段的內(nèi)存位置,也可以修改對象的字段值,即使它是私有的;
/**
* 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ù)
通過park方法掛起當(dāng)前調(diào)用線程,通過unpark恢復(fù)一個線程(參數(shù)),線程操作相關(guān)還有一個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操作
是通過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);