这个帖子是关于JAVA中不为人知的特性的后续更新,若是想获得下次在线讨论的更新,请经过邮件订阅,而且不要忘了在评论区留下你的意见和建议。java
Java是一个安全的开发工具,它阻止开发人员犯不少低级的错误,而大部份的错误都是基于内存管理方面的。若是你想搞破坏,可使用Unsafe这个类。这个类是属于sun.* API中的类,而且它不是J2SE中真正的一部份,所以你可能找不到任何的官方文档,更可悲的是,它也没有比较好的代码文档。git
实例化sun.misc.Unsafe程序员
若是你尝试建立Unsafe类的实例,基于如下两种缘由是不被容许的。github
1)、Unsafe类的构造函数是私有的;数组
2)、虽然它有静态的getUnsafe()方法,可是若是你尝试调用Unsafe.getUnsafe(),会获得一个SecutiryException。这个类只有被JDK信任的类实例化。安全
可是这总会是有变通的解决办法的,一个简单的方式就是使用反射进行实例化:数据结构
Field f = Unsafe.class.getDeclaredField("theUnsafe"); //Internal reference f.setAccessible(true); Unsafe unsafe = (Unsafe) f.get(null);
注:IDE如Eclipse对会这样的使用报错,不过不用担忧,直接运行代码就行,能够正常运行的。app
(译者注:还有一种解决方案,就是将Eclipse中这种限制获取由错误,修改成警告,具体操做为将Windows->Preference...->Java->Compiler->Errors/Warnings中的"Deprecated and restricted API",级别由Error修改成Warning就能够了)ide
如今进入主题,使用这个对象咱们能够作以下“有趣的”事情。函数
使用sun.misc.Unsafe
1)、突破限制建立实例
经过allocateInstance()方法,你能够建立一个类的实例,可是却不须要调用它的构造函数、初使化代码、各类JVM安全检查以及其它的一些底层的东西。即便构造函数是私有,咱们也能够经过这个方法建立它的实例。
(这个对单例模式情有独钟的程序员来讲将会是一个噩梦,它们没有办法阻止这种方式调用)
看下面一个实例(注:为了配合这个主题,译者将原实例中的public构造函数修改成了私有的):
public class UnsafeDemo {
public static void main(String[] args) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException, InstantiationException {
Field f = Unsafe.class.getDeclaredField("theUnsafe"); // Internal reference
f.setAccessible(true);
Unsafe unsafe = (Unsafe) f.get(null);
// This creates an instance of player class without any initialization
Player p = (Player) unsafe.allocateInstance(Player.class);
System.out.println(p.getAge()); // Print 0
p.setAge(45); // Let's now set age 45 to un-initialized object
System.out.println(p.getAge()); // Print 45
}
}
class Player {
private int age = 12;
private Player() {
this.age = 50;
}
public int getAge() {
return this.age;
}
public void setAge(int age) {
this.age = age;
}
}
2)、使用直接获取内存的方式实现浅克隆
如何实现浅克隆?在clone(){...}方法中调用super.clone(),对吗?这里存在的问题是首先你必须继续Cloneable接口,而且在全部你须要作浅克隆的对象中实现clone()方法,对于一个懒懒的程序员来讲,这个工做量太大了。
我不推荐上面的作法而是直接使用Unsafe,咱们能够仅使用几行代码就实现浅克隆,而且它能够像某些工具类同样用于任意类的克隆。
这个戏法就是把一个对象的字节码拷贝到内存的另一个地方,而后再将这个对象转换为被克隆的对象类型。
3)、来自黑客的密码安全
这个好似颇有趣吧?实事就是这样的。开发人员建立密码或者是保证密码到字符串中,而后在应用程序的代码中使用这些密码,使用事后,聪明的程序员会把字符串的引用设为NULL,所以它就不会被引用着而且很容易被垃圾收集器给回收掉。
可是从你将引用设为NULL到被垃圾收集器收集的这个时间段以内(原文:But from the time, you made the reference null to the time garbage collector kicks in),它是处于字符串池中的,而且在你系统中进行一个复杂的攻击(原文:And a sophisticated attack on your system),也是能够读取到你的内存区域而且得到密码,虽然机会很小,可是老是存在的。
这就是为何建议使用char[]数组存放密码,当使用完事后,你能够迭代处理当前数组,修改/清空这些字符。
另一个方式就是使用魔术类Unsafe。你能够建立另一个和当前密码字符串具备相同长度的临时字符串,将临时密码中的每一个字符都设值为"?"或者"*"(任何字符均可以),当你完成密码的逻辑后,你只须要简单的将临时密码中的字节数组拷贝到原始的密码串中,这就是使用临时密码覆盖真实的密码。
示例代码可能会是这样:
[java] view plaincopy
String password = new String("l00k@myHor$e");
String fake = new String(password.replaceAll(".", "?"));
System.out.println(password); // l00k@myHor$e
System.out.println(fake); // ????????????
getUnsafe().copyMemory(fake, 0L, null, toAddress(password), sizeOf(password));
System.out.println(password); // ????????????
System.out.println(fake); // ????????????
运行时动态建立类
咱们能够在运行时运态的建立类,例如经过编译后的.class文件,操做方式就是将.class文件读取到字节数据组中,并将其传到defineClass方法中。
[java] view plaincopy
//Sample code to craeet classes
byte[] classContents = getClassContent();
Class c = getUnsafe().defineClass(null, classContents, 0, classContents.length);
c.getMethod("a").invoke(c.newInstance(), null);
//Method to read .class file
private static byte[] getClassContent() throws Exception {
File f = new File("/home/mishadoff/tmp/A.class");
FileInputStream input = new FileInputStream(f);
byte[] content = new byte[(int)f.length()];
input.read(content);
input.close();
return content;
}
4)、超大数组
从所周知,常量Integer.MAX_VALUE是JAVA中数组长度的最大值,若是你想建立一个很是大的数组(虽然在一般的应用中不可能会用上),能够经过对内存进行直接分配实现。
下面这个示例将会建立分配一段连续的内存(数组),它的容易是容许最大容量的两倍。
[java] view plaincopy
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;
}
}
应用示例
[java] view plaincopy
long SUPER_SIZE = (long)Integer.MAX_VALUE * 2;
SuperArray array = new SuperArray(SUPER_SIZE);
System.out.println("Array size:" + array.size()); // 4294967294
for (int i = 0; i < 100; i++) {
array.set((long)Integer.MAX_VALUE + i, (byte)3);
sum += array.get((long)Integer.MAX_VALUE + i);
}
System.out.println("Sum of 100 elements:" + sum); // 300
但请注意这可能会致使JVM挂掉。
结束语
sun.misc.Unsafe provides almost unlimited capabilities for exploring and modification of VM’s runtime data structures. Despite the fact that these capabilities are almost inapplicable in Java development itself, Unsafe is a great tool for anyone who want to study HotSpot VM without C++ code debugging or need to create ad hoc profiling instruments.
sun.misc.Unsafe提供了能够随意查看及修改JVM中运行时的数据结构,尽管这些功能在JAVA开发自己是不适用的,Unsafe是一个用于研究学习HotSpot虚拟机很是棒的工具,由于它不须要调用C++代码,或者须要建立即时分析的工具。
参考
http://mishadoff.github.io/blog/java-magic-part-4-sun-dot-misc-dot-unsafe/