Java企业教程系列
Unsafe是什么?
Java虚拟机最大的竞争对手可能是C#语言的CLR。CLR允许编写不安全的低层次代码作为入门编程,但是这很难在JVM上实现。如果你需要用Java这样的特殊功能,你可能会被迫使用JNI,这又需要你要知道一些C,而且代码会紧耦合到一个特定平台。sun.misc.Unsafe是另一个替代在Java plarform上使用Java API进行低级编程的工具,kryo和Twitter的Storm都是基于此库。它也即将成为 public API in Java 9.
sun.misc.Unsafe类是由核心Java类使用,因此,它的构造器是私有的,只增加一个私有的单例实例,公有的getter方法会执行一个安全检查,以避免它被公开使用:
public static Unsafe getUnsafe() {
Class cc = sun.reflect.Reflection.getCallerClass(2);
if (cc.getClassLoader() != null)
throw new SecurityException("Unsafe");
return theUnsafe;
}
该方法首先从当前线程的方法堆栈查找调用类。这个查询是由sun.reflection.Reflection另一个内部类实现,基本上向下浏览调用堆栈帧然后返回方法定义的类。这种安全检查不过可能在未来的版本中改变。
当浏览堆栈时,第一个找到的类(索引0 )是Reflection类自己,第二个(索引1)将是Unsafe类,,索引2将持有你的调用Unsafe# getUnsafe ( )的应用程序类 。然后检查其类加载器直至最上层空引用。
由于在类加载器中没有一个核心Java类,因此永远不能够直接调用此方法,因为会收到的抛出SecurityException作为回答。 (从技术上讲,你可能会迫使虚拟机使用引导类加载器通过将其添加到- Xbootclasspath上加载应用程序的类,但这需要应用程序代码之外的一些设置。 )因此,下面的测试一定会成功:
@Test(expected = SecurityException.class)
public void testSingletonGetter() throws Exception {
Unsafe.getUnsafe();
}
我们可以使用反射机制对其私有构造器进行构造,从而获得Unsafe实例:
Constructor<Unsafe> unsafeConstructor = Unsafe.class.getDeclaredConstructor();
unsafeConstructor.setAccessible(true);
Unsafe unsafe = unsafeConstructor.newInstance();
直接操作内存
使用直接Unsafe分配和释放内存以及写和读。通过下面方式获得Unsafe实例:
private static Unsafe getUnsafe() throws Exception {
// Get the Unsafe object instance
Field field = sun.misc.Unsafe.class.getDeclaredField("theUnsafe");
field.setAccessible(true);
return (sun.misc.Unsafe) field.get(null);
}
直接从内存读写, 通过其方法allocateMemory获得内存地址,通过putAddress 或putByte 写内存即可:
public static void showBytes() {
try {
Unsafe unsafe = getUnsafe();
// Writing to a memory - MAX VALUE Byte
byte value = Byte.MAX_VALUE;
long bytes = 1;
// Allocate given memory size
long memoryAddress = unsafe.allocateMemory(bytes);
// Write value to the allocated memory
unsafe.putAddress(memoryAddress, value); // or putByte
// Output the value written and the memory address
System.out.println("[Byte] Writing " + value + " under the " + memoryAddress + " address.");
long readValue = unsafe.getAddress(memoryAddress); // or getByte
// Output the value from
System.out.println("[Byte] Reading " + readValue + " from the " + memoryAddress + " address.");
// C style! Release the Kraken... Memory!! :)
unsafe.freeMemory(memoryAddress);
} catch (Exception e) {
e.printStackTrace();
}
}
从内存直接写一个Long值:
private static void showLong() {
try {
Unsafe unsafe = getUnsafe();
// Writing to a memory - MAX VALUE of Long
long value = Long.MAX_VALUE;
long bytes = Long.SIZE;
// Allocate given memory size
long memoryAddress = unsafe.allocateMemory(bytes);
// Write value to the allocated memory
unsafe.putLong(memoryAddress, value);
// Output the value written and the memory address
System.out.println("[Long] Writing " + value + " under the " + memoryAddress + " address.");
// Read the value from the memory
long readValue = unsafe.getLong(memoryAddress);
// Output the value from
System.out.println("[Long] Reading " + readValue + " from the " + memoryAddress + " address.");
// C style! Release the Kraken... Memory!! :)
unsafe.freeMemory(memoryAddress);
} catch (Exception e) {
e.printStackTrace();
}
}
重新分配内存:
public static void showDontFreeMemory() {
for (int t = 0; t < 100; t++) {
new Thread() {
public void run() {
System.out.println("Thread " + Thread.currentThread().getName() + " start!");
for (int i = 0; i < 1000000; i++) {
try {
Unsafe unsafe = getUnsafe();
// Writing random Long to a memory
long value = new Random().nextLong();
long bytes = Long.SIZE;
// Allocate given memory size
long memoryAddress = unsafe.allocateMemory(bytes);
// Write value to the allocated memory
unsafe.putLong(memoryAddress, value);
// Read the value from the memory
long readValue = unsafe.getLong(memoryAddress);
// Always free the memory !!
// ... FIXME: deallocate the memory used
} catch (Exception e) {
e.printStackTrace();
}
}
System.out.println("Thread " + Thread.currentThread().getName() + " stop!");
};
}.start();
}
}