java是一种安全的编程语言,并且禁止编程人员对内存做一些胡乱的操作。如果编程人员像操作,可以通过unsafe类
如果我们想使用unsafe类,首先要创建一个unsafe对象。但是unsafe的构造方法是私有的。Unsafe类有个getUnsafe方法可以获取unsafe对象,但是他会坚持classloader
public static Unsafe getUnsafe( ) {
Class cc = sun.reflect.Reflection.getCallerClass(2);
if (cc.getClassLoader() != null)
throw new SecurityException("Unsafe");
return theUnsafe;
}
java通过检查我们的代码是否是通过主classloader加载的来判断代码是否安全。
在程序运行时,我们可以通过bootclasspath来是我们代码授信。
java -Xbootclasspath:/usr/jdk1.7.0/jre/lib/rt.jar:. com.mishadoff.magic.UnsafeClient
但是这种方式很麻烦。
Unsafe 有一个私有实例theUnsafe,我们可以通过反射获取
// 通过反射得到theUnsafe对应的Field对象
Field field = Unsafe.class.getDeclaredField("theUnsafe");
// 设置该Field为可访问
field.setAccessible(true);
// 通过Field得到该Field对应的具体对象,传入null是因为该Field为static的
Unsafe unsafe = (Unsafe) field.get(null);
Unsafe API
获取表层内存信息
- addressSize
- pageSize
对象和属性操作
- allocateInstance
- objectFieldOffset
类和静态属性的操作
- staticFieldOffset
- defineClass
- defineAnonymousClass
- ensureClassInitialized
数组操作
- arrayBaseOffset
- arrayIndexScale
同步操作
- monitorEnter
- tryMonitorEnter
- monitorExit
- compareAndSwapInt
- putOrderedInt
直接操作内存
- allocateMemory
- copyMemory
- freeMemory
- getAddress
- getInt
- putInt
实例化对象
通过allocateInstance()方法,你可以创建一个类的实例,但是却不需要调用它的构造函数、初使化代码、各种JVM安全检查以及其它的一些底层的东西。即使构造函数是私有,我们也可以通过这个方法创建它的实例。
class A {
private long a; // not initialized value
public A( ) {
this.a = 1; // initialization
}
public long a( ) { return this.a; }
}
使用构造方法、反射和unsafe得到的结果不同
A o1 = new A(); // constructor
o1.a(); // prints 1
A o2 = A.class.newInstance(); // reflection
o2.a(); // prints 1
A o3 = (A) unsafe.allocateInstance(A.class); // unsafe
o3.a(); // prints 0
实现超大数组
从所周知,常量Integer.MAX_VALUE是JAVA中数组长度的最大值,如果你想创建一个非常大的数组(虽然在通常的应用中不可能会用上),可以通过对内存进行直接分配实现。
class SuperArray {
private final static int BYTE = 1;
private long size;
private long address;
public SuperArray(long size) {
this.size = size;
//得到分配内存的起始地址
address = getUnsafe().allocateMemory(size * BYTE);
}
public void set(long i, byte value) {
getUnsafe().putByte(address + i * BYTE, value);
}
public int get(long idx) {
return getUnsafe().getByte(address + idx * BYTE);
}
public long size() {
return size;
}
}
求对象大小
通过objectFieldOffset方法实现C预约的sizeOf方法。得到包括父类在内的所有非静态属性,找到最大值并对齐填充
public static long sizeOf(Object o) {
Unsafe u = getUnsafe();
HashSet<Field> fields = new HashSet<Field>();
Class c = o.getClass();
while (c != Object.class) {
for (Field f : c.getDeclaredFields()) {
if ((f.getModifiers() & Modifier.STATIC) == 0) {
fields.add(f);
}
}
c = c.getSuperclass();
}
// get offset
long maxSize = 0;
for (Field f : fields) {
long offset = u.objectFieldOffset(f);
if (offset > maxSize) {
maxSize = offset;
}
}
return ((maxSize/8) + 1) * 8; // padding
}
参考资料:
http://mishadoff.com/blog/java-magic-part-4-sun-dot-misc-dot-unsafe/