unsafe入门

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 fields = new HashSet();
    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/

你可能感兴趣的:(java)