Java是以安全著称,但在Java中有一个类是一个Bug级别的存在,那就是Unsafe. 前面已经说过Unsafe在java中的使用,此处我们直接说用法:   
避免初始化
当你想跳过对象初始化的阶段,或者绕过构造函数的检查,去实例化没有任何公共构造函数的类,可以使用allocateInstance:   
| 12
 3
 4
 5
 6
 7
 
 | class A {private long a;
 public A() {
 this.a = 1;
 }
 public long a() { return this.a; }
 }
 
 | 
使用构造函数、反射和unsafe初始化它,将得到不同的结果。
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 
 | public static void main(String[] args) throws IllegalAccessException, InstantiationException {A o1 = new A();
 System.out.println(o1.a());
 
 A o2 = A.class.newInstance();
 System.out.println(o2.a());
 
 A o3 = (A) UnsafeUtils.getUnsafe().allocateInstance(A.class);
 System.out.println(o3.a());
 }
 
 | 
内存崩溃(Memory corruption)
我们可以使用Unsafe去做一些绕过安全的技术:    
| 12
 3
 4
 5
 6
 7
 
 | class Guard {private int ACCESS_ALLOWED = 1;
 
 public boolean giveAccess() {
 return 42 == ACCESS_ALLOWED;
 }
 }
 
 | 
 当客户端调用giveAccess代码是,始终返回的都是false.使用Unsafe可以绕过权限:   
| 12
 3
 4
 5
 
 | Guard guard=new Guard();System.out.println("修改前:"+guard.giveAccess());
 Field f = guard.getClass().getDeclaredField("ACCESS_ALLOWED");
 unsafe.putInt(guard,  unsafe.objectFieldOffset(f), 42);
 System.out.println("修改后:"+guard.giveAccess());
 
 | 
计算对象的大小
sizeOf是返回对象的自身内存大小。    
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 
 | public static long sizeOf(Object o) {Unsafe u = UnsafeUtils.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();
 }
 
 long maxSize = 0;
 for (Field f : fields) {
 long offset = u.objectFieldOffset(f);
 if (offset > maxSize) {
 maxSize = offset;
 }
 }
 
 return ((maxSize/8) + 1) * 8;
 }
 
 
 | 
如果只是对象类的结构大小,那么可以更简单的实现:
| 12
 3
 4
 5
 6
 7
 8
 9
 
 | public static long sizeOf(Object object){return UnsafeUtils.getUnsafe().getAddress(
 normalize(UnsafeUtils.getUnsafe().getInt(object, 4L)) + 12L);
 }
 private static long normalize(int value) {
 if(value >= 0) return value;
 return (~0L >>> 32) & value;
 }
 
 
 | 
浅拷贝(Shallow copy)
为了计算自身内存的大小,可以简单的添加拷贝的对象方法,标准的解决方案是使用Cloneable修改代码,或者实现自定义的拷贝方法.   
浅拷贝:  
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 
 | public class ShallowCopy {static   Unsafe unsafe = UnsafeUtils.getUnsafe();
 public static Object shallowCopy(Object obj) {
 long size = sizeOf(obj);
 long start = toAddress(obj);
 long address = unsafe.allocateMemory(size);
 unsafe.copyMemory(start, address, size);
 return fromAddress(address);
 }
 
 static long toAddress(Object obj) {
 Object[] array = new Object[] {obj};
 long baseOffset = unsafe.arrayBaseOffset(Object[].class);
 return normalize(unsafe.getInt(array, baseOffset));
 }
 
 static Object fromAddress(long address) {
 Object[] array = new Object[] {null};
 long baseOffset = unsafe.arrayBaseOffset(Object[].class);
 unsafe.putLong(array, baseOffset, address);
 return array[0];
 }
 public static long sizeOf(Object o) {
 
 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();
 }
 
 
 long maxSize = 0;
 for (Field f : fields) {
 long offset = unsafe.objectFieldOffset(f);
 if (offset > maxSize) {
 maxSize = offset;
 }
 }
 return ((maxSize/8) + 1) * 8;
 }
 
 private static long normalize(int value) {
 if(value >= 0) return value;
 return (~0L >>> 32) & value;
 }
 }
 
 
 | 
隐藏密码(Hide Password)
在Unsafe类中可以删除内存中的对象,比如密码信息。用户的密码大多数都是byte[]或char[]数组,为什么要使用数组呢?   
这个是出于安全的考虑,因为我们可以删除不需要的数组元素,如果是一个字符串对象的话,这可以像一个对象在内存中保存,删除该对象只是删除了引用,真正的数据还保存在内存中。
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 
 | String password = new String("l00k@myHor$e");
 String fake = new String(password.replaceAll(".", "?"));
 System.out.println(password);
 System.out.println(fake);
 
 unsafe.copyMemory(
 fake, 0L, null, toAddress(password), sizeOf(password));
 
 System.out.println(password);
 System.out.println(fake);
 
 
 |