本文基于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类提供了硬件级别的原子操作,主要提供了以下功能:
- 通过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();
- 可以定位对象某字段的内存位置,也可以修改对象的字段值,即使它是私有的;
/**
* 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);
- 挂起与恢复
通过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");
}
}
- 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);